roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item 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                     var h = ce.getHeight();
3871                     Roo.log(h);
3872                     ce.removeClass('show');
3873                     // at this point we should be able to see it..
3874                     ce.addClass('collapsing');
3875                     
3876                     ce.setHeight(0); // resize it ...
3877                      (function() {
3878                         Roo.log('done transition');
3879                         ce.removeClass('collapsing');
3880                         ce.addClass('show');
3881                         ce.removeClass('collapse');
3882
3883                         ce.dom.style.height = '';
3884                     }).defer(500);
3885                     ce.setHeight(h);
3886                     
3887                 } else {
3888                     ce.addClass('collapsing');
3889                     ce.removeClass('show');
3890                     (function() {
3891                         ce.removeClass('collapsing');
3892                         ce.addClass('collapse');
3893                         
3894                     }).defer(200);
3895                     
3896                 }
3897             }
3898             
3899         }, this);
3900         
3901         var mark = {
3902             tag: "div",
3903             cls:"x-dlg-mask"
3904         };
3905         
3906         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3907         
3908         var size = this.el.getSize();
3909         this.maskEl.setSize(size.width, size.height);
3910         this.maskEl.enableDisplayMode("block");
3911         this.maskEl.hide();
3912         
3913         if(this.loadMask){
3914             this.maskEl.show();
3915         }
3916     },
3917     
3918     
3919     getChildContainer : function()
3920     {
3921         if (this.el.select('.collapse').getCount()) {
3922             return this.el.select('.collapse',true).first();
3923         }
3924         
3925         return this.el;
3926     },
3927     
3928     mask : function()
3929     {
3930         this.maskEl.show();
3931     },
3932     
3933     unmask : function()
3934     {
3935         this.maskEl.hide();
3936     } 
3937     
3938     
3939     
3940     
3941 });
3942
3943
3944
3945  
3946
3947  /*
3948  * - LGPL
3949  *
3950  * navbar
3951  * 
3952  */
3953
3954 /**
3955  * @class Roo.bootstrap.NavSimplebar
3956  * @extends Roo.bootstrap.Navbar
3957  * Bootstrap Sidebar class
3958  *
3959  * @cfg {Boolean} inverse is inverted color
3960  * 
3961  * @cfg {String} type (nav | pills | tabs)
3962  * @cfg {Boolean} arrangement stacked | justified
3963  * @cfg {String} align (left | right) alignment
3964  * 
3965  * @cfg {Boolean} main (true|false) main nav bar? default false
3966  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3967  * 
3968  * @cfg {String} tag (header|footer|nav|div) default is nav 
3969
3970  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3971  * 
3972  * 
3973  * @constructor
3974  * Create a new Sidebar
3975  * @param {Object} config The config object
3976  */
3977
3978
3979 Roo.bootstrap.NavSimplebar = function(config){
3980     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3981 };
3982
3983 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3984     
3985     inverse: false,
3986     
3987     type: false,
3988     arrangement: '',
3989     align : false,
3990     
3991     weight : 'light',
3992     
3993     main : false,
3994     
3995     
3996     tag : false,
3997     
3998     
3999     getAutoCreate : function(){
4000         
4001         
4002         var cfg = {
4003             tag : this.tag || 'div',
4004             cls : 'navbar navbar-expand-lg'
4005         };
4006         if (['light','white'].indexOf(this.weight) > -1) {
4007             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4008         }
4009         cfg.cls += ' bg-' + this.weight;
4010         
4011           
4012         
4013         cfg.cn = [
4014             {
4015                 cls: 'nav',
4016                 tag : 'ul'
4017             }
4018         ];
4019         
4020          
4021         this.type = this.type || 'nav';
4022         if (['tabs','pills'].indexOf(this.type)!==-1) {
4023             cfg.cn[0].cls += ' nav-' + this.type
4024         
4025         
4026         } else {
4027             if (this.type!=='nav') {
4028                 Roo.log('nav type must be nav/tabs/pills')
4029             }
4030             cfg.cn[0].cls += ' navbar-nav'
4031         }
4032         
4033         
4034         
4035         
4036         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4037             cfg.cn[0].cls += ' nav-' + this.arrangement;
4038         }
4039         
4040         
4041         if (this.align === 'right') {
4042             cfg.cn[0].cls += ' navbar-right';
4043         }
4044         
4045         if (this.inverse) {
4046             cfg.cls += ' navbar-inverse';
4047             
4048         }
4049         
4050         
4051         return cfg;
4052     
4053         
4054     }
4055     
4056     
4057     
4058 });
4059
4060
4061
4062  
4063
4064  
4065        /*
4066  * - LGPL
4067  *
4068  * navbar
4069  * navbar-fixed-top
4070  * navbar-expand-md  fixed-top 
4071  */
4072
4073 /**
4074  * @class Roo.bootstrap.NavHeaderbar
4075  * @extends Roo.bootstrap.NavSimplebar
4076  * Bootstrap Sidebar class
4077  *
4078  * @cfg {String} brand what is brand
4079  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4080  * @cfg {String} brand_href href of the brand
4081  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4082  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4083  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4084  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4085  * 
4086  * @constructor
4087  * Create a new Sidebar
4088  * @param {Object} config The config object
4089  */
4090
4091
4092 Roo.bootstrap.NavHeaderbar = function(config){
4093     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4094       
4095 };
4096
4097 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4098     
4099     position: '',
4100     brand: '',
4101     brand_href: false,
4102     srButton : true,
4103     autohide : false,
4104     desktopCenter : false,
4105    
4106     
4107     getAutoCreate : function(){
4108         
4109         var   cfg = {
4110             tag: this.nav || 'nav',
4111             cls: 'navbar navbar-expand-md',
4112             role: 'navigation',
4113             cn: []
4114         };
4115         
4116         var cn = cfg.cn;
4117         if (this.desktopCenter) {
4118             cn.push({cls : 'container', cn : []});
4119             cn = cn[0].cn;
4120         }
4121         
4122         if(this.srButton){
4123             var btn = {
4124                 tag: 'button',
4125                 type: 'button',
4126                 cls: 'navbar-toggle navbar-toggler',
4127                 'data-toggle': 'collapse',
4128                 cn: [
4129                     {
4130                         tag: 'span',
4131                         cls: 'sr-only',
4132                         html: 'Toggle navigation'
4133                     },
4134                     {
4135                         tag: 'span',
4136                         cls: 'icon-bar navbar-toggler-icon'
4137                     },
4138                     {
4139                         tag: 'span',
4140                         cls: 'icon-bar'
4141                     },
4142                     {
4143                         tag: 'span',
4144                         cls: 'icon-bar'
4145                     }
4146                 ]
4147             };
4148             
4149             cn.push( Roo.bootstrap.version == 4 ? btn : {
4150                 tag: 'div',
4151                 cls: 'navbar-header',
4152                 cn: [
4153                     btn
4154                 ]
4155             });
4156         }
4157         
4158         cn.push({
4159             tag: 'div',
4160             cls: 'collapse navbar-collapse',
4161             cn : []
4162         });
4163         
4164         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4165         
4166         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4167             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4168             
4169             // tag can override this..
4170             
4171             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4172         }
4173         
4174         if (this.brand !== '') {
4175             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4176             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4177                 tag: 'a',
4178                 href: this.brand_href ? this.brand_href : '#',
4179                 cls: 'navbar-brand',
4180                 cn: [
4181                 this.brand
4182                 ]
4183             });
4184         }
4185         
4186         if(this.main){
4187             cfg.cls += ' main-nav';
4188         }
4189         
4190         
4191         return cfg;
4192
4193         
4194     },
4195     getHeaderChildContainer : function()
4196     {
4197         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4198             return this.el.select('.navbar-header',true).first();
4199         }
4200         
4201         return this.getChildContainer();
4202     },
4203     
4204     
4205     initEvents : function()
4206     {
4207         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4208         
4209         if (this.autohide) {
4210             
4211             var prevScroll = 0;
4212             var ft = this.el;
4213             
4214             Roo.get(document).on('scroll',function(e) {
4215                 var ns = Roo.get(document).getScroll().top;
4216                 var os = prevScroll;
4217                 prevScroll = ns;
4218                 
4219                 if(ns > os){
4220                     ft.removeClass('slideDown');
4221                     ft.addClass('slideUp');
4222                     return;
4223                 }
4224                 ft.removeClass('slideUp');
4225                 ft.addClass('slideDown');
4226                  
4227               
4228           },this);
4229         }
4230     }    
4231     
4232 });
4233
4234
4235
4236  
4237
4238  /*
4239  * - LGPL
4240  *
4241  * navbar
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavSidebar
4247  * @extends Roo.bootstrap.Navbar
4248  * Bootstrap Sidebar class
4249  * 
4250  * @constructor
4251  * Create a new Sidebar
4252  * @param {Object} config The config object
4253  */
4254
4255
4256 Roo.bootstrap.NavSidebar = function(config){
4257     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4258 };
4259
4260 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4261     
4262     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4263     
4264     getAutoCreate : function(){
4265         
4266         
4267         return  {
4268             tag: 'div',
4269             cls: 'sidebar sidebar-nav'
4270         };
4271     
4272         
4273     }
4274     
4275     
4276     
4277 });
4278
4279
4280
4281  
4282
4283  /*
4284  * - LGPL
4285  *
4286  * nav group
4287  * 
4288  */
4289
4290 /**
4291  * @class Roo.bootstrap.NavGroup
4292  * @extends Roo.bootstrap.Component
4293  * Bootstrap NavGroup class
4294  * @cfg {String} align (left|right)
4295  * @cfg {Boolean} inverse
4296  * @cfg {String} type (nav|pills|tab) default nav
4297  * @cfg {String} navId - reference Id for navbar.
4298
4299  * 
4300  * @constructor
4301  * Create a new nav group
4302  * @param {Object} config The config object
4303  */
4304
4305 Roo.bootstrap.NavGroup = function(config){
4306     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4307     this.navItems = [];
4308    
4309     Roo.bootstrap.NavGroup.register(this);
4310      this.addEvents({
4311         /**
4312              * @event changed
4313              * Fires when the active item changes
4314              * @param {Roo.bootstrap.NavGroup} this
4315              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4316              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4317          */
4318         'changed': true
4319      });
4320     
4321 };
4322
4323 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4324     
4325     align: '',
4326     inverse: false,
4327     form: false,
4328     type: 'nav',
4329     navId : '',
4330     // private
4331     
4332     navItems : false, 
4333     
4334     getAutoCreate : function()
4335     {
4336         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4337         
4338         cfg = {
4339             tag : 'ul',
4340             cls: 'nav' 
4341         };
4342         
4343         if (['tabs','pills'].indexOf(this.type)!==-1) {
4344             cfg.cls += ' nav-' + this.type
4345         } else {
4346             if (this.type!=='nav') {
4347                 Roo.log('nav type must be nav/tabs/pills')
4348             }
4349             cfg.cls += ' navbar-nav'
4350         }
4351         
4352         if (this.parent() && this.parent().sidebar) {
4353             cfg = {
4354                 tag: 'ul',
4355                 cls: 'dashboard-menu sidebar-menu'
4356             };
4357             
4358             return cfg;
4359         }
4360         
4361         if (this.form === true) {
4362             cfg = {
4363                 tag: 'form',
4364                 cls: 'navbar-form'
4365             };
4366             
4367             if (this.align === 'right') {
4368                 cfg.cls += ' navbar-right ml-md-auto';
4369             } else {
4370                 cfg.cls += ' navbar-left';
4371             }
4372         }
4373         
4374         if (this.align === 'right') {
4375             cfg.cls += ' navbar-right ml-md-auto';
4376         } else {
4377             cfg.cls += ' mr-auto';
4378         }
4379         
4380         if (this.inverse) {
4381             cfg.cls += ' navbar-inverse';
4382             
4383         }
4384         
4385         
4386         return cfg;
4387     },
4388     /**
4389     * sets the active Navigation item
4390     * @param {Roo.bootstrap.NavItem} the new current navitem
4391     */
4392     setActiveItem : function(item)
4393     {
4394         var prev = false;
4395         Roo.each(this.navItems, function(v){
4396             if (v == item) {
4397                 return ;
4398             }
4399             if (v.isActive()) {
4400                 v.setActive(false, true);
4401                 prev = v;
4402                 
4403             }
4404             
4405         });
4406
4407         item.setActive(true, true);
4408         this.fireEvent('changed', this, item, prev);
4409         
4410         
4411     },
4412     /**
4413     * gets the active Navigation item
4414     * @return {Roo.bootstrap.NavItem} the current navitem
4415     */
4416     getActive : function()
4417     {
4418         
4419         var prev = false;
4420         Roo.each(this.navItems, function(v){
4421             
4422             if (v.isActive()) {
4423                 prev = v;
4424                 
4425             }
4426             
4427         });
4428         return prev;
4429     },
4430     
4431     indexOfNav : function()
4432     {
4433         
4434         var prev = false;
4435         Roo.each(this.navItems, function(v,i){
4436             
4437             if (v.isActive()) {
4438                 prev = i;
4439                 
4440             }
4441             
4442         });
4443         return prev;
4444     },
4445     /**
4446     * adds a Navigation item
4447     * @param {Roo.bootstrap.NavItem} the navitem to add
4448     */
4449     addItem : function(cfg)
4450     {
4451         var cn = new Roo.bootstrap.NavItem(cfg);
4452         this.register(cn);
4453         cn.parentId = this.id;
4454         cn.onRender(this.el, null);
4455         return cn;
4456     },
4457     /**
4458     * register a Navigation item
4459     * @param {Roo.bootstrap.NavItem} the navitem to add
4460     */
4461     register : function(item)
4462     {
4463         this.navItems.push( item);
4464         item.navId = this.navId;
4465     
4466     },
4467     
4468     /**
4469     * clear all the Navigation item
4470     */
4471    
4472     clearAll : function()
4473     {
4474         this.navItems = [];
4475         this.el.dom.innerHTML = '';
4476     },
4477     
4478     getNavItem: function(tabId)
4479     {
4480         var ret = false;
4481         Roo.each(this.navItems, function(e) {
4482             if (e.tabId == tabId) {
4483                ret =  e;
4484                return false;
4485             }
4486             return true;
4487             
4488         });
4489         return ret;
4490     },
4491     
4492     setActiveNext : function()
4493     {
4494         var i = this.indexOfNav(this.getActive());
4495         if (i > this.navItems.length) {
4496             return;
4497         }
4498         this.setActiveItem(this.navItems[i+1]);
4499     },
4500     setActivePrev : function()
4501     {
4502         var i = this.indexOfNav(this.getActive());
4503         if (i  < 1) {
4504             return;
4505         }
4506         this.setActiveItem(this.navItems[i-1]);
4507     },
4508     clearWasActive : function(except) {
4509         Roo.each(this.navItems, function(e) {
4510             if (e.tabId != except.tabId && e.was_active) {
4511                e.was_active = false;
4512                return false;
4513             }
4514             return true;
4515             
4516         });
4517     },
4518     getWasActive : function ()
4519     {
4520         var r = false;
4521         Roo.each(this.navItems, function(e) {
4522             if (e.was_active) {
4523                r = e;
4524                return false;
4525             }
4526             return true;
4527             
4528         });
4529         return r;
4530     }
4531     
4532     
4533 });
4534
4535  
4536 Roo.apply(Roo.bootstrap.NavGroup, {
4537     
4538     groups: {},
4539      /**
4540     * register a Navigation Group
4541     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4542     */
4543     register : function(navgrp)
4544     {
4545         this.groups[navgrp.navId] = navgrp;
4546         
4547     },
4548     /**
4549     * fetch a Navigation Group based on the navigation ID
4550     * @param {string} the navgroup to add
4551     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4552     */
4553     get: function(navId) {
4554         if (typeof(this.groups[navId]) == 'undefined') {
4555             return false;
4556             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4557         }
4558         return this.groups[navId] ;
4559     }
4560     
4561     
4562     
4563 });
4564
4565  /*
4566  * - LGPL
4567  *
4568  * row
4569  * 
4570  */
4571
4572 /**
4573  * @class Roo.bootstrap.NavItem
4574  * @extends Roo.bootstrap.Component
4575  * Bootstrap Navbar.NavItem class
4576  * @cfg {String} href  link to
4577  * @cfg {String} html content of button
4578  * @cfg {String} badge text inside badge
4579  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4580  * @cfg {String} glyphicon DEPRICATED - use fa
4581  * @cfg {String} icon DEPRICATED - use fa
4582  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4583  * @cfg {Boolean} active Is item active
4584  * @cfg {Boolean} disabled Is item disabled
4585  
4586  * @cfg {Boolean} preventDefault (true | false) default false
4587  * @cfg {String} tabId the tab that this item activates.
4588  * @cfg {String} tagtype (a|span) render as a href or span?
4589  * @cfg {Boolean} animateRef (true|false) link to element default false  
4590   
4591  * @constructor
4592  * Create a new Navbar Item
4593  * @param {Object} config The config object
4594  */
4595 Roo.bootstrap.NavItem = function(config){
4596     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4597     this.addEvents({
4598         // raw events
4599         /**
4600          * @event click
4601          * The raw click event for the entire grid.
4602          * @param {Roo.EventObject} e
4603          */
4604         "click" : true,
4605          /**
4606             * @event changed
4607             * Fires when the active item active state changes
4608             * @param {Roo.bootstrap.NavItem} this
4609             * @param {boolean} state the new state
4610              
4611          */
4612         'changed': true,
4613         /**
4614             * @event scrollto
4615             * Fires when scroll to element
4616             * @param {Roo.bootstrap.NavItem} this
4617             * @param {Object} options
4618             * @param {Roo.EventObject} e
4619              
4620          */
4621         'scrollto': true
4622     });
4623    
4624 };
4625
4626 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4627     
4628     href: false,
4629     html: '',
4630     badge: '',
4631     icon: false,
4632     fa : false,
4633     glyphicon: false,
4634     active: false,
4635     preventDefault : false,
4636     tabId : false,
4637     tagtype : 'a',
4638     disabled : false,
4639     animateRef : false,
4640     was_active : false,
4641     
4642     getAutoCreate : function(){
4643          
4644         var cfg = {
4645             tag: 'li',
4646             cls: 'nav-item'
4647             
4648         };
4649         
4650         if (this.active) {
4651             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4652         }
4653         if (this.disabled) {
4654             cfg.cls += ' disabled';
4655         }
4656         
4657         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4658             cfg.cn = [
4659                 {
4660                     tag: this.tagtype,
4661                     href : this.href || "#",
4662                     html: this.html || ''
4663                 }
4664             ];
4665             if (this.tagtype == 'a') {
4666                 cfg.cn[0].cls = 'nav-link';
4667             }
4668             if (this.icon) {
4669                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4670             }
4671             if (this.fa) {
4672                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4673             }
4674             if(this.glyphicon) {
4675                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4676             }
4677             
4678             if (this.menu) {
4679                 
4680                 cfg.cn[0].html += " <span class='caret'></span>";
4681              
4682             }
4683             
4684             if (this.badge !== '') {
4685                  
4686                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4687             }
4688         }
4689         
4690         
4691         
4692         return cfg;
4693     },
4694     initEvents: function() 
4695     {
4696         if (typeof (this.menu) != 'undefined') {
4697             this.menu.parentType = this.xtype;
4698             this.menu.triggerEl = this.el;
4699             this.menu = this.addxtype(Roo.apply({}, this.menu));
4700         }
4701         
4702         this.el.select('a',true).on('click', this.onClick, this);
4703         
4704         if(this.tagtype == 'span'){
4705             this.el.select('span',true).on('click', this.onClick, this);
4706         }
4707        
4708         // at this point parent should be available..
4709         this.parent().register(this);
4710     },
4711     
4712     onClick : function(e)
4713     {
4714         if (e.getTarget('.dropdown-menu-item')) {
4715             // did you click on a menu itemm.... - then don't trigger onclick..
4716             return;
4717         }
4718         
4719         if(
4720                 this.preventDefault || 
4721                 this.href == '#' 
4722         ){
4723             Roo.log("NavItem - prevent Default?");
4724             e.preventDefault();
4725         }
4726         
4727         if (this.disabled) {
4728             return;
4729         }
4730         
4731         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4732         if (tg && tg.transition) {
4733             Roo.log("waiting for the transitionend");
4734             return;
4735         }
4736         
4737         
4738         
4739         //Roo.log("fire event clicked");
4740         if(this.fireEvent('click', this, e) === false){
4741             return;
4742         };
4743         
4744         if(this.tagtype == 'span'){
4745             return;
4746         }
4747         
4748         //Roo.log(this.href);
4749         var ael = this.el.select('a',true).first();
4750         //Roo.log(ael);
4751         
4752         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4753             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4754             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4755                 return; // ignore... - it's a 'hash' to another page.
4756             }
4757             Roo.log("NavItem - prevent Default?");
4758             e.preventDefault();
4759             this.scrollToElement(e);
4760         }
4761         
4762         
4763         var p =  this.parent();
4764    
4765         if (['tabs','pills'].indexOf(p.type)!==-1) {
4766             if (typeof(p.setActiveItem) !== 'undefined') {
4767                 p.setActiveItem(this);
4768             }
4769         }
4770         
4771         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4772         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4773             // remove the collapsed menu expand...
4774             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4775         }
4776     },
4777     
4778     isActive: function () {
4779         return this.active
4780     },
4781     setActive : function(state, fire, is_was_active)
4782     {
4783         if (this.active && !state && this.navId) {
4784             this.was_active = true;
4785             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4786             if (nv) {
4787                 nv.clearWasActive(this);
4788             }
4789             
4790         }
4791         this.active = state;
4792         
4793         if (!state ) {
4794             this.el.removeClass('active');
4795         } else if (!this.el.hasClass('active')) {
4796             this.el.addClass('active');
4797         }
4798         if (fire) {
4799             this.fireEvent('changed', this, state);
4800         }
4801         
4802         // show a panel if it's registered and related..
4803         
4804         if (!this.navId || !this.tabId || !state || is_was_active) {
4805             return;
4806         }
4807         
4808         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4809         if (!tg) {
4810             return;
4811         }
4812         var pan = tg.getPanelByName(this.tabId);
4813         if (!pan) {
4814             return;
4815         }
4816         // if we can not flip to new panel - go back to old nav highlight..
4817         if (false == tg.showPanel(pan)) {
4818             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4819             if (nv) {
4820                 var onav = nv.getWasActive();
4821                 if (onav) {
4822                     onav.setActive(true, false, true);
4823                 }
4824             }
4825             
4826         }
4827         
4828         
4829         
4830     },
4831      // this should not be here...
4832     setDisabled : function(state)
4833     {
4834         this.disabled = state;
4835         if (!state ) {
4836             this.el.removeClass('disabled');
4837         } else if (!this.el.hasClass('disabled')) {
4838             this.el.addClass('disabled');
4839         }
4840         
4841     },
4842     
4843     /**
4844      * Fetch the element to display the tooltip on.
4845      * @return {Roo.Element} defaults to this.el
4846      */
4847     tooltipEl : function()
4848     {
4849         return this.el.select('' + this.tagtype + '', true).first();
4850     },
4851     
4852     scrollToElement : function(e)
4853     {
4854         var c = document.body;
4855         
4856         /*
4857          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4858          */
4859         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4860             c = document.documentElement;
4861         }
4862         
4863         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4864         
4865         if(!target){
4866             return;
4867         }
4868
4869         var o = target.calcOffsetsTo(c);
4870         
4871         var options = {
4872             target : target,
4873             value : o[1]
4874         };
4875         
4876         this.fireEvent('scrollto', this, options, e);
4877         
4878         Roo.get(c).scrollTo('top', options.value, true);
4879         
4880         return;
4881     }
4882 });
4883  
4884
4885  /*
4886  * - LGPL
4887  *
4888  * sidebar item
4889  *
4890  *  li
4891  *    <span> icon </span>
4892  *    <span> text </span>
4893  *    <span>badge </span>
4894  */
4895
4896 /**
4897  * @class Roo.bootstrap.NavSidebarItem
4898  * @extends Roo.bootstrap.NavItem
4899  * Bootstrap Navbar.NavSidebarItem class
4900  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4901  * {Boolean} open is the menu open
4902  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4903  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4904  * {String} buttonSize (sm|md|lg)the extra classes for the button
4905  * {Boolean} showArrow show arrow next to the text (default true)
4906  * @constructor
4907  * Create a new Navbar Button
4908  * @param {Object} config The config object
4909  */
4910 Roo.bootstrap.NavSidebarItem = function(config){
4911     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4912     this.addEvents({
4913         // raw events
4914         /**
4915          * @event click
4916          * The raw click event for the entire grid.
4917          * @param {Roo.EventObject} e
4918          */
4919         "click" : true,
4920          /**
4921             * @event changed
4922             * Fires when the active item active state changes
4923             * @param {Roo.bootstrap.NavSidebarItem} this
4924             * @param {boolean} state the new state
4925              
4926          */
4927         'changed': true
4928     });
4929    
4930 };
4931
4932 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4933     
4934     badgeWeight : 'default',
4935     
4936     open: false,
4937     
4938     buttonView : false,
4939     
4940     buttonWeight : 'default',
4941     
4942     buttonSize : 'md',
4943     
4944     showArrow : true,
4945     
4946     getAutoCreate : function(){
4947         
4948         
4949         var a = {
4950                 tag: 'a',
4951                 href : this.href || '#',
4952                 cls: '',
4953                 html : '',
4954                 cn : []
4955         };
4956         
4957         if(this.buttonView){
4958             a = {
4959                 tag: 'button',
4960                 href : this.href || '#',
4961                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4962                 html : this.html,
4963                 cn : []
4964             };
4965         }
4966         
4967         var cfg = {
4968             tag: 'li',
4969             cls: '',
4970             cn: [ a ]
4971         };
4972         
4973         if (this.active) {
4974             cfg.cls += ' active';
4975         }
4976         
4977         if (this.disabled) {
4978             cfg.cls += ' disabled';
4979         }
4980         if (this.open) {
4981             cfg.cls += ' open x-open';
4982         }
4983         // left icon..
4984         if (this.glyphicon || this.icon) {
4985             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4986             a.cn.push({ tag : 'i', cls : c }) ;
4987         }
4988         
4989         if(!this.buttonView){
4990             var span = {
4991                 tag: 'span',
4992                 html : this.html || ''
4993             };
4994
4995             a.cn.push(span);
4996             
4997         }
4998         
4999         if (this.badge !== '') {
5000             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5001         }
5002         
5003         if (this.menu) {
5004             
5005             if(this.showArrow){
5006                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5007             }
5008             
5009             a.cls += ' dropdown-toggle treeview' ;
5010         }
5011         
5012         return cfg;
5013     },
5014     
5015     initEvents : function()
5016     { 
5017         if (typeof (this.menu) != 'undefined') {
5018             this.menu.parentType = this.xtype;
5019             this.menu.triggerEl = this.el;
5020             this.menu = this.addxtype(Roo.apply({}, this.menu));
5021         }
5022         
5023         this.el.on('click', this.onClick, this);
5024         
5025         if(this.badge !== ''){
5026             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5027         }
5028         
5029     },
5030     
5031     onClick : function(e)
5032     {
5033         if(this.disabled){
5034             e.preventDefault();
5035             return;
5036         }
5037         
5038         if(this.preventDefault){
5039             e.preventDefault();
5040         }
5041         
5042         this.fireEvent('click', this);
5043     },
5044     
5045     disable : function()
5046     {
5047         this.setDisabled(true);
5048     },
5049     
5050     enable : function()
5051     {
5052         this.setDisabled(false);
5053     },
5054     
5055     setDisabled : function(state)
5056     {
5057         if(this.disabled == state){
5058             return;
5059         }
5060         
5061         this.disabled = state;
5062         
5063         if (state) {
5064             this.el.addClass('disabled');
5065             return;
5066         }
5067         
5068         this.el.removeClass('disabled');
5069         
5070         return;
5071     },
5072     
5073     setActive : function(state)
5074     {
5075         if(this.active == state){
5076             return;
5077         }
5078         
5079         this.active = state;
5080         
5081         if (state) {
5082             this.el.addClass('active');
5083             return;
5084         }
5085         
5086         this.el.removeClass('active');
5087         
5088         return;
5089     },
5090     
5091     isActive: function () 
5092     {
5093         return this.active;
5094     },
5095     
5096     setBadge : function(str)
5097     {
5098         if(!this.badgeEl){
5099             return;
5100         }
5101         
5102         this.badgeEl.dom.innerHTML = str;
5103     }
5104     
5105    
5106      
5107  
5108 });
5109  
5110
5111  /*
5112  * - LGPL
5113  *
5114  * row
5115  * 
5116  */
5117
5118 /**
5119  * @class Roo.bootstrap.Row
5120  * @extends Roo.bootstrap.Component
5121  * Bootstrap Row class (contains columns...)
5122  * 
5123  * @constructor
5124  * Create a new Row
5125  * @param {Object} config The config object
5126  */
5127
5128 Roo.bootstrap.Row = function(config){
5129     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5130 };
5131
5132 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5133     
5134     getAutoCreate : function(){
5135        return {
5136             cls: 'row clearfix'
5137        };
5138     }
5139     
5140     
5141 });
5142
5143  
5144
5145  /*
5146  * - LGPL
5147  *
5148  * element
5149  * 
5150  */
5151
5152 /**
5153  * @class Roo.bootstrap.Element
5154  * @extends Roo.bootstrap.Component
5155  * Bootstrap Element class
5156  * @cfg {String} html contents of the element
5157  * @cfg {String} tag tag of the element
5158  * @cfg {String} cls class of the element
5159  * @cfg {Boolean} preventDefault (true|false) default false
5160  * @cfg {Boolean} clickable (true|false) default false
5161  * 
5162  * @constructor
5163  * Create a new Element
5164  * @param {Object} config The config object
5165  */
5166
5167 Roo.bootstrap.Element = function(config){
5168     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5169     
5170     this.addEvents({
5171         // raw events
5172         /**
5173          * @event click
5174          * When a element is chick
5175          * @param {Roo.bootstrap.Element} this
5176          * @param {Roo.EventObject} e
5177          */
5178         "click" : true
5179     });
5180 };
5181
5182 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5183     
5184     tag: 'div',
5185     cls: '',
5186     html: '',
5187     preventDefault: false, 
5188     clickable: false,
5189     
5190     getAutoCreate : function(){
5191         
5192         var cfg = {
5193             tag: this.tag,
5194             // cls: this.cls, double assign in parent class Component.js :: onRender
5195             html: this.html
5196         };
5197         
5198         return cfg;
5199     },
5200     
5201     initEvents: function() 
5202     {
5203         Roo.bootstrap.Element.superclass.initEvents.call(this);
5204         
5205         if(this.clickable){
5206             this.el.on('click', this.onClick, this);
5207         }
5208         
5209     },
5210     
5211     onClick : function(e)
5212     {
5213         if(this.preventDefault){
5214             e.preventDefault();
5215         }
5216         
5217         this.fireEvent('click', this, e);
5218     },
5219     
5220     getValue : function()
5221     {
5222         return this.el.dom.innerHTML;
5223     },
5224     
5225     setValue : function(value)
5226     {
5227         this.el.dom.innerHTML = value;
5228     }
5229    
5230 });
5231
5232  
5233
5234  /*
5235  * - LGPL
5236  *
5237  * pagination
5238  * 
5239  */
5240
5241 /**
5242  * @class Roo.bootstrap.Pagination
5243  * @extends Roo.bootstrap.Component
5244  * Bootstrap Pagination class
5245  * @cfg {String} size xs | sm | md | lg
5246  * @cfg {Boolean} inverse false | true
5247  * 
5248  * @constructor
5249  * Create a new Pagination
5250  * @param {Object} config The config object
5251  */
5252
5253 Roo.bootstrap.Pagination = function(config){
5254     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5255 };
5256
5257 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5258     
5259     cls: false,
5260     size: false,
5261     inverse: false,
5262     
5263     getAutoCreate : function(){
5264         var cfg = {
5265             tag: 'ul',
5266                 cls: 'pagination'
5267         };
5268         if (this.inverse) {
5269             cfg.cls += ' inverse';
5270         }
5271         if (this.html) {
5272             cfg.html=this.html;
5273         }
5274         if (this.cls) {
5275             cfg.cls += " " + this.cls;
5276         }
5277         return cfg;
5278     }
5279    
5280 });
5281
5282  
5283
5284  /*
5285  * - LGPL
5286  *
5287  * Pagination item
5288  * 
5289  */
5290
5291
5292 /**
5293  * @class Roo.bootstrap.PaginationItem
5294  * @extends Roo.bootstrap.Component
5295  * Bootstrap PaginationItem class
5296  * @cfg {String} html text
5297  * @cfg {String} href the link
5298  * @cfg {Boolean} preventDefault (true | false) default true
5299  * @cfg {Boolean} active (true | false) default false
5300  * @cfg {Boolean} disabled default false
5301  * 
5302  * 
5303  * @constructor
5304  * Create a new PaginationItem
5305  * @param {Object} config The config object
5306  */
5307
5308
5309 Roo.bootstrap.PaginationItem = function(config){
5310     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5311     this.addEvents({
5312         // raw events
5313         /**
5314          * @event click
5315          * The raw click event for the entire grid.
5316          * @param {Roo.EventObject} e
5317          */
5318         "click" : true
5319     });
5320 };
5321
5322 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5323     
5324     href : false,
5325     html : false,
5326     preventDefault: true,
5327     active : false,
5328     cls : false,
5329     disabled: false,
5330     
5331     getAutoCreate : function(){
5332         var cfg= {
5333             tag: 'li',
5334             cn: [
5335                 {
5336                     tag : 'a',
5337                     href : this.href ? this.href : '#',
5338                     html : this.html ? this.html : ''
5339                 }
5340             ]
5341         };
5342         
5343         if(this.cls){
5344             cfg.cls = this.cls;
5345         }
5346         
5347         if(this.disabled){
5348             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5349         }
5350         
5351         if(this.active){
5352             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5353         }
5354         
5355         return cfg;
5356     },
5357     
5358     initEvents: function() {
5359         
5360         this.el.on('click', this.onClick, this);
5361         
5362     },
5363     onClick : function(e)
5364     {
5365         Roo.log('PaginationItem on click ');
5366         if(this.preventDefault){
5367             e.preventDefault();
5368         }
5369         
5370         if(this.disabled){
5371             return;
5372         }
5373         
5374         this.fireEvent('click', this, e);
5375     }
5376    
5377 });
5378
5379  
5380
5381  /*
5382  * - LGPL
5383  *
5384  * slider
5385  * 
5386  */
5387
5388
5389 /**
5390  * @class Roo.bootstrap.Slider
5391  * @extends Roo.bootstrap.Component
5392  * Bootstrap Slider class
5393  *    
5394  * @constructor
5395  * Create a new Slider
5396  * @param {Object} config The config object
5397  */
5398
5399 Roo.bootstrap.Slider = function(config){
5400     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5401 };
5402
5403 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5404     
5405     getAutoCreate : function(){
5406         
5407         var cfg = {
5408             tag: 'div',
5409             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5410             cn: [
5411                 {
5412                     tag: 'a',
5413                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5414                 }
5415             ]
5416         };
5417         
5418         return cfg;
5419     }
5420    
5421 });
5422
5423  /*
5424  * Based on:
5425  * Ext JS Library 1.1.1
5426  * Copyright(c) 2006-2007, Ext JS, LLC.
5427  *
5428  * Originally Released Under LGPL - original licence link has changed is not relivant.
5429  *
5430  * Fork - LGPL
5431  * <script type="text/javascript">
5432  */
5433  
5434
5435 /**
5436  * @class Roo.grid.ColumnModel
5437  * @extends Roo.util.Observable
5438  * This is the default implementation of a ColumnModel used by the Grid. It defines
5439  * the columns in the grid.
5440  * <br>Usage:<br>
5441  <pre><code>
5442  var colModel = new Roo.grid.ColumnModel([
5443         {header: "Ticker", width: 60, sortable: true, locked: true},
5444         {header: "Company Name", width: 150, sortable: true},
5445         {header: "Market Cap.", width: 100, sortable: true},
5446         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5447         {header: "Employees", width: 100, sortable: true, resizable: false}
5448  ]);
5449  </code></pre>
5450  * <p>
5451  
5452  * The config options listed for this class are options which may appear in each
5453  * individual column definition.
5454  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5455  * @constructor
5456  * @param {Object} config An Array of column config objects. See this class's
5457  * config objects for details.
5458 */
5459 Roo.grid.ColumnModel = function(config){
5460         /**
5461      * The config passed into the constructor
5462      */
5463     this.config = config;
5464     this.lookup = {};
5465
5466     // if no id, create one
5467     // if the column does not have a dataIndex mapping,
5468     // map it to the order it is in the config
5469     for(var i = 0, len = config.length; i < len; i++){
5470         var c = config[i];
5471         if(typeof c.dataIndex == "undefined"){
5472             c.dataIndex = i;
5473         }
5474         if(typeof c.renderer == "string"){
5475             c.renderer = Roo.util.Format[c.renderer];
5476         }
5477         if(typeof c.id == "undefined"){
5478             c.id = Roo.id();
5479         }
5480         if(c.editor && c.editor.xtype){
5481             c.editor  = Roo.factory(c.editor, Roo.grid);
5482         }
5483         if(c.editor && c.editor.isFormField){
5484             c.editor = new Roo.grid.GridEditor(c.editor);
5485         }
5486         this.lookup[c.id] = c;
5487     }
5488
5489     /**
5490      * The width of columns which have no width specified (defaults to 100)
5491      * @type Number
5492      */
5493     this.defaultWidth = 100;
5494
5495     /**
5496      * Default sortable of columns which have no sortable specified (defaults to false)
5497      * @type Boolean
5498      */
5499     this.defaultSortable = false;
5500
5501     this.addEvents({
5502         /**
5503              * @event widthchange
5504              * Fires when the width of a column changes.
5505              * @param {ColumnModel} this
5506              * @param {Number} columnIndex The column index
5507              * @param {Number} newWidth The new width
5508              */
5509             "widthchange": true,
5510         /**
5511              * @event headerchange
5512              * Fires when the text of a header changes.
5513              * @param {ColumnModel} this
5514              * @param {Number} columnIndex The column index
5515              * @param {Number} newText The new header text
5516              */
5517             "headerchange": true,
5518         /**
5519              * @event hiddenchange
5520              * Fires when a column is hidden or "unhidden".
5521              * @param {ColumnModel} this
5522              * @param {Number} columnIndex The column index
5523              * @param {Boolean} hidden true if hidden, false otherwise
5524              */
5525             "hiddenchange": true,
5526             /**
5527          * @event columnmoved
5528          * Fires when a column is moved.
5529          * @param {ColumnModel} this
5530          * @param {Number} oldIndex
5531          * @param {Number} newIndex
5532          */
5533         "columnmoved" : true,
5534         /**
5535          * @event columlockchange
5536          * Fires when a column's locked state is changed
5537          * @param {ColumnModel} this
5538          * @param {Number} colIndex
5539          * @param {Boolean} locked true if locked
5540          */
5541         "columnlockchange" : true
5542     });
5543     Roo.grid.ColumnModel.superclass.constructor.call(this);
5544 };
5545 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5546     /**
5547      * @cfg {String} header The header text to display in the Grid view.
5548      */
5549     /**
5550      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5551      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5552      * specified, the column's index is used as an index into the Record's data Array.
5553      */
5554     /**
5555      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5556      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5557      */
5558     /**
5559      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5560      * Defaults to the value of the {@link #defaultSortable} property.
5561      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5562      */
5563     /**
5564      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5565      */
5566     /**
5567      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5568      */
5569     /**
5570      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5571      */
5572     /**
5573      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5574      */
5575     /**
5576      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5577      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5578      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5579      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5580      */
5581        /**
5582      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5583      */
5584     /**
5585      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5586      */
5587     /**
5588      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5589      */
5590     /**
5591      * @cfg {String} cursor (Optional)
5592      */
5593     /**
5594      * @cfg {String} tooltip (Optional)
5595      */
5596     /**
5597      * @cfg {Number} xs (Optional)
5598      */
5599     /**
5600      * @cfg {Number} sm (Optional)
5601      */
5602     /**
5603      * @cfg {Number} md (Optional)
5604      */
5605     /**
5606      * @cfg {Number} lg (Optional)
5607      */
5608     /**
5609      * Returns the id of the column at the specified index.
5610      * @param {Number} index The column index
5611      * @return {String} the id
5612      */
5613     getColumnId : function(index){
5614         return this.config[index].id;
5615     },
5616
5617     /**
5618      * Returns the column for a specified id.
5619      * @param {String} id The column id
5620      * @return {Object} the column
5621      */
5622     getColumnById : function(id){
5623         return this.lookup[id];
5624     },
5625
5626     
5627     /**
5628      * Returns the column for a specified dataIndex.
5629      * @param {String} dataIndex The column dataIndex
5630      * @return {Object|Boolean} the column or false if not found
5631      */
5632     getColumnByDataIndex: function(dataIndex){
5633         var index = this.findColumnIndex(dataIndex);
5634         return index > -1 ? this.config[index] : false;
5635     },
5636     
5637     /**
5638      * Returns the index for a specified column id.
5639      * @param {String} id The column id
5640      * @return {Number} the index, or -1 if not found
5641      */
5642     getIndexById : function(id){
5643         for(var i = 0, len = this.config.length; i < len; i++){
5644             if(this.config[i].id == id){
5645                 return i;
5646             }
5647         }
5648         return -1;
5649     },
5650     
5651     /**
5652      * Returns the index for a specified column dataIndex.
5653      * @param {String} dataIndex The column dataIndex
5654      * @return {Number} the index, or -1 if not found
5655      */
5656     
5657     findColumnIndex : function(dataIndex){
5658         for(var i = 0, len = this.config.length; i < len; i++){
5659             if(this.config[i].dataIndex == dataIndex){
5660                 return i;
5661             }
5662         }
5663         return -1;
5664     },
5665     
5666     
5667     moveColumn : function(oldIndex, newIndex){
5668         var c = this.config[oldIndex];
5669         this.config.splice(oldIndex, 1);
5670         this.config.splice(newIndex, 0, c);
5671         this.dataMap = null;
5672         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5673     },
5674
5675     isLocked : function(colIndex){
5676         return this.config[colIndex].locked === true;
5677     },
5678
5679     setLocked : function(colIndex, value, suppressEvent){
5680         if(this.isLocked(colIndex) == value){
5681             return;
5682         }
5683         this.config[colIndex].locked = value;
5684         if(!suppressEvent){
5685             this.fireEvent("columnlockchange", this, colIndex, value);
5686         }
5687     },
5688
5689     getTotalLockedWidth : function(){
5690         var totalWidth = 0;
5691         for(var i = 0; i < this.config.length; i++){
5692             if(this.isLocked(i) && !this.isHidden(i)){
5693                 this.totalWidth += this.getColumnWidth(i);
5694             }
5695         }
5696         return totalWidth;
5697     },
5698
5699     getLockedCount : function(){
5700         for(var i = 0, len = this.config.length; i < len; i++){
5701             if(!this.isLocked(i)){
5702                 return i;
5703             }
5704         }
5705         
5706         return this.config.length;
5707     },
5708
5709     /**
5710      * Returns the number of columns.
5711      * @return {Number}
5712      */
5713     getColumnCount : function(visibleOnly){
5714         if(visibleOnly === true){
5715             var c = 0;
5716             for(var i = 0, len = this.config.length; i < len; i++){
5717                 if(!this.isHidden(i)){
5718                     c++;
5719                 }
5720             }
5721             return c;
5722         }
5723         return this.config.length;
5724     },
5725
5726     /**
5727      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5728      * @param {Function} fn
5729      * @param {Object} scope (optional)
5730      * @return {Array} result
5731      */
5732     getColumnsBy : function(fn, scope){
5733         var r = [];
5734         for(var i = 0, len = this.config.length; i < len; i++){
5735             var c = this.config[i];
5736             if(fn.call(scope||this, c, i) === true){
5737                 r[r.length] = c;
5738             }
5739         }
5740         return r;
5741     },
5742
5743     /**
5744      * Returns true if the specified column is sortable.
5745      * @param {Number} col The column index
5746      * @return {Boolean}
5747      */
5748     isSortable : function(col){
5749         if(typeof this.config[col].sortable == "undefined"){
5750             return this.defaultSortable;
5751         }
5752         return this.config[col].sortable;
5753     },
5754
5755     /**
5756      * Returns the rendering (formatting) function defined for the column.
5757      * @param {Number} col The column index.
5758      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5759      */
5760     getRenderer : function(col){
5761         if(!this.config[col].renderer){
5762             return Roo.grid.ColumnModel.defaultRenderer;
5763         }
5764         return this.config[col].renderer;
5765     },
5766
5767     /**
5768      * Sets the rendering (formatting) function for a column.
5769      * @param {Number} col The column index
5770      * @param {Function} fn The function to use to process the cell's raw data
5771      * to return HTML markup for the grid view. The render function is called with
5772      * the following parameters:<ul>
5773      * <li>Data value.</li>
5774      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5775      * <li>css A CSS style string to apply to the table cell.</li>
5776      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5777      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5778      * <li>Row index</li>
5779      * <li>Column index</li>
5780      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5781      */
5782     setRenderer : function(col, fn){
5783         this.config[col].renderer = fn;
5784     },
5785
5786     /**
5787      * Returns the width for the specified column.
5788      * @param {Number} col The column index
5789      * @return {Number}
5790      */
5791     getColumnWidth : function(col){
5792         return this.config[col].width * 1 || this.defaultWidth;
5793     },
5794
5795     /**
5796      * Sets the width for a column.
5797      * @param {Number} col The column index
5798      * @param {Number} width The new width
5799      */
5800     setColumnWidth : function(col, width, suppressEvent){
5801         this.config[col].width = width;
5802         this.totalWidth = null;
5803         if(!suppressEvent){
5804              this.fireEvent("widthchange", this, col, width);
5805         }
5806     },
5807
5808     /**
5809      * Returns the total width of all columns.
5810      * @param {Boolean} includeHidden True to include hidden column widths
5811      * @return {Number}
5812      */
5813     getTotalWidth : function(includeHidden){
5814         if(!this.totalWidth){
5815             this.totalWidth = 0;
5816             for(var i = 0, len = this.config.length; i < len; i++){
5817                 if(includeHidden || !this.isHidden(i)){
5818                     this.totalWidth += this.getColumnWidth(i);
5819                 }
5820             }
5821         }
5822         return this.totalWidth;
5823     },
5824
5825     /**
5826      * Returns the header for the specified column.
5827      * @param {Number} col The column index
5828      * @return {String}
5829      */
5830     getColumnHeader : function(col){
5831         return this.config[col].header;
5832     },
5833
5834     /**
5835      * Sets the header for a column.
5836      * @param {Number} col The column index
5837      * @param {String} header The new header
5838      */
5839     setColumnHeader : function(col, header){
5840         this.config[col].header = header;
5841         this.fireEvent("headerchange", this, col, header);
5842     },
5843
5844     /**
5845      * Returns the tooltip for the specified column.
5846      * @param {Number} col The column index
5847      * @return {String}
5848      */
5849     getColumnTooltip : function(col){
5850             return this.config[col].tooltip;
5851     },
5852     /**
5853      * Sets the tooltip for a column.
5854      * @param {Number} col The column index
5855      * @param {String} tooltip The new tooltip
5856      */
5857     setColumnTooltip : function(col, tooltip){
5858             this.config[col].tooltip = tooltip;
5859     },
5860
5861     /**
5862      * Returns the dataIndex for the specified column.
5863      * @param {Number} col The column index
5864      * @return {Number}
5865      */
5866     getDataIndex : function(col){
5867         return this.config[col].dataIndex;
5868     },
5869
5870     /**
5871      * Sets the dataIndex for a column.
5872      * @param {Number} col The column index
5873      * @param {Number} dataIndex The new dataIndex
5874      */
5875     setDataIndex : function(col, dataIndex){
5876         this.config[col].dataIndex = dataIndex;
5877     },
5878
5879     
5880     
5881     /**
5882      * Returns true if the cell is editable.
5883      * @param {Number} colIndex The column index
5884      * @param {Number} rowIndex The row index - this is nto actually used..?
5885      * @return {Boolean}
5886      */
5887     isCellEditable : function(colIndex, rowIndex){
5888         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5889     },
5890
5891     /**
5892      * Returns the editor defined for the cell/column.
5893      * return false or null to disable editing.
5894      * @param {Number} colIndex The column index
5895      * @param {Number} rowIndex The row index
5896      * @return {Object}
5897      */
5898     getCellEditor : function(colIndex, rowIndex){
5899         return this.config[colIndex].editor;
5900     },
5901
5902     /**
5903      * Sets if a column is editable.
5904      * @param {Number} col The column index
5905      * @param {Boolean} editable True if the column is editable
5906      */
5907     setEditable : function(col, editable){
5908         this.config[col].editable = editable;
5909     },
5910
5911
5912     /**
5913      * Returns true if the column is hidden.
5914      * @param {Number} colIndex The column index
5915      * @return {Boolean}
5916      */
5917     isHidden : function(colIndex){
5918         return this.config[colIndex].hidden;
5919     },
5920
5921
5922     /**
5923      * Returns true if the column width cannot be changed
5924      */
5925     isFixed : function(colIndex){
5926         return this.config[colIndex].fixed;
5927     },
5928
5929     /**
5930      * Returns true if the column can be resized
5931      * @return {Boolean}
5932      */
5933     isResizable : function(colIndex){
5934         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5935     },
5936     /**
5937      * Sets if a column is hidden.
5938      * @param {Number} colIndex The column index
5939      * @param {Boolean} hidden True if the column is hidden
5940      */
5941     setHidden : function(colIndex, hidden){
5942         this.config[colIndex].hidden = hidden;
5943         this.totalWidth = null;
5944         this.fireEvent("hiddenchange", this, colIndex, hidden);
5945     },
5946
5947     /**
5948      * Sets the editor for a column.
5949      * @param {Number} col The column index
5950      * @param {Object} editor The editor object
5951      */
5952     setEditor : function(col, editor){
5953         this.config[col].editor = editor;
5954     }
5955 });
5956
5957 Roo.grid.ColumnModel.defaultRenderer = function(value)
5958 {
5959     if(typeof value == "object") {
5960         return value;
5961     }
5962         if(typeof value == "string" && value.length < 1){
5963             return "&#160;";
5964         }
5965     
5966         return String.format("{0}", value);
5967 };
5968
5969 // Alias for backwards compatibility
5970 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5971 /*
5972  * Based on:
5973  * Ext JS Library 1.1.1
5974  * Copyright(c) 2006-2007, Ext JS, LLC.
5975  *
5976  * Originally Released Under LGPL - original licence link has changed is not relivant.
5977  *
5978  * Fork - LGPL
5979  * <script type="text/javascript">
5980  */
5981  
5982 /**
5983  * @class Roo.LoadMask
5984  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5985  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5986  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5987  * element's UpdateManager load indicator and will be destroyed after the initial load.
5988  * @constructor
5989  * Create a new LoadMask
5990  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5991  * @param {Object} config The config object
5992  */
5993 Roo.LoadMask = function(el, config){
5994     this.el = Roo.get(el);
5995     Roo.apply(this, config);
5996     if(this.store){
5997         this.store.on('beforeload', this.onBeforeLoad, this);
5998         this.store.on('load', this.onLoad, this);
5999         this.store.on('loadexception', this.onLoadException, this);
6000         this.removeMask = false;
6001     }else{
6002         var um = this.el.getUpdateManager();
6003         um.showLoadIndicator = false; // disable the default indicator
6004         um.on('beforeupdate', this.onBeforeLoad, this);
6005         um.on('update', this.onLoad, this);
6006         um.on('failure', this.onLoad, this);
6007         this.removeMask = true;
6008     }
6009 };
6010
6011 Roo.LoadMask.prototype = {
6012     /**
6013      * @cfg {Boolean} removeMask
6014      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6015      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6016      */
6017     /**
6018      * @cfg {String} msg
6019      * The text to display in a centered loading message box (defaults to 'Loading...')
6020      */
6021     msg : 'Loading...',
6022     /**
6023      * @cfg {String} msgCls
6024      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6025      */
6026     msgCls : 'x-mask-loading',
6027
6028     /**
6029      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6030      * @type Boolean
6031      */
6032     disabled: false,
6033
6034     /**
6035      * Disables the mask to prevent it from being displayed
6036      */
6037     disable : function(){
6038        this.disabled = true;
6039     },
6040
6041     /**
6042      * Enables the mask so that it can be displayed
6043      */
6044     enable : function(){
6045         this.disabled = false;
6046     },
6047     
6048     onLoadException : function()
6049     {
6050         Roo.log(arguments);
6051         
6052         if (typeof(arguments[3]) != 'undefined') {
6053             Roo.MessageBox.alert("Error loading",arguments[3]);
6054         } 
6055         /*
6056         try {
6057             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6058                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6059             }   
6060         } catch(e) {
6061             
6062         }
6063         */
6064     
6065         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6066     },
6067     // private
6068     onLoad : function()
6069     {
6070         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6071     },
6072
6073     // private
6074     onBeforeLoad : function(){
6075         if(!this.disabled){
6076             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6077         }
6078     },
6079
6080     // private
6081     destroy : function(){
6082         if(this.store){
6083             this.store.un('beforeload', this.onBeforeLoad, this);
6084             this.store.un('load', this.onLoad, this);
6085             this.store.un('loadexception', this.onLoadException, this);
6086         }else{
6087             var um = this.el.getUpdateManager();
6088             um.un('beforeupdate', this.onBeforeLoad, this);
6089             um.un('update', this.onLoad, this);
6090             um.un('failure', this.onLoad, this);
6091         }
6092     }
6093 };/*
6094  * - LGPL
6095  *
6096  * table
6097  * 
6098  */
6099
6100 /**
6101  * @class Roo.bootstrap.Table
6102  * @extends Roo.bootstrap.Component
6103  * Bootstrap Table class
6104  * @cfg {String} cls table class
6105  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6106  * @cfg {String} bgcolor Specifies the background color for a table
6107  * @cfg {Number} border Specifies whether the table cells should have borders or not
6108  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6109  * @cfg {Number} cellspacing Specifies the space between cells
6110  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6111  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6112  * @cfg {String} sortable Specifies that the table should be sortable
6113  * @cfg {String} summary Specifies a summary of the content of a table
6114  * @cfg {Number} width Specifies the width of a table
6115  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6116  * 
6117  * @cfg {boolean} striped Should the rows be alternative striped
6118  * @cfg {boolean} bordered Add borders to the table
6119  * @cfg {boolean} hover Add hover highlighting
6120  * @cfg {boolean} condensed Format condensed
6121  * @cfg {boolean} responsive Format condensed
6122  * @cfg {Boolean} loadMask (true|false) default false
6123  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6124  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6125  * @cfg {Boolean} rowSelection (true|false) default false
6126  * @cfg {Boolean} cellSelection (true|false) default false
6127  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6128  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6129  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6130  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6131  
6132  * 
6133  * @constructor
6134  * Create a new Table
6135  * @param {Object} config The config object
6136  */
6137
6138 Roo.bootstrap.Table = function(config){
6139     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6140     
6141   
6142     
6143     // BC...
6144     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6145     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6146     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6147     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6148     
6149     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6150     if (this.sm) {
6151         this.sm.grid = this;
6152         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6153         this.sm = this.selModel;
6154         this.sm.xmodule = this.xmodule || false;
6155     }
6156     
6157     if (this.cm && typeof(this.cm.config) == 'undefined') {
6158         this.colModel = new Roo.grid.ColumnModel(this.cm);
6159         this.cm = this.colModel;
6160         this.cm.xmodule = this.xmodule || false;
6161     }
6162     if (this.store) {
6163         this.store= Roo.factory(this.store, Roo.data);
6164         this.ds = this.store;
6165         this.ds.xmodule = this.xmodule || false;
6166          
6167     }
6168     if (this.footer && this.store) {
6169         this.footer.dataSource = this.ds;
6170         this.footer = Roo.factory(this.footer);
6171     }
6172     
6173     /** @private */
6174     this.addEvents({
6175         /**
6176          * @event cellclick
6177          * Fires when a cell is clicked
6178          * @param {Roo.bootstrap.Table} this
6179          * @param {Roo.Element} el
6180          * @param {Number} rowIndex
6181          * @param {Number} columnIndex
6182          * @param {Roo.EventObject} e
6183          */
6184         "cellclick" : true,
6185         /**
6186          * @event celldblclick
6187          * Fires when a cell is double clicked
6188          * @param {Roo.bootstrap.Table} this
6189          * @param {Roo.Element} el
6190          * @param {Number} rowIndex
6191          * @param {Number} columnIndex
6192          * @param {Roo.EventObject} e
6193          */
6194         "celldblclick" : true,
6195         /**
6196          * @event rowclick
6197          * Fires when a row is clicked
6198          * @param {Roo.bootstrap.Table} this
6199          * @param {Roo.Element} el
6200          * @param {Number} rowIndex
6201          * @param {Roo.EventObject} e
6202          */
6203         "rowclick" : true,
6204         /**
6205          * @event rowdblclick
6206          * Fires when a row is double clicked
6207          * @param {Roo.bootstrap.Table} this
6208          * @param {Roo.Element} el
6209          * @param {Number} rowIndex
6210          * @param {Roo.EventObject} e
6211          */
6212         "rowdblclick" : true,
6213         /**
6214          * @event mouseover
6215          * Fires when a mouseover occur
6216          * @param {Roo.bootstrap.Table} this
6217          * @param {Roo.Element} el
6218          * @param {Number} rowIndex
6219          * @param {Number} columnIndex
6220          * @param {Roo.EventObject} e
6221          */
6222         "mouseover" : true,
6223         /**
6224          * @event mouseout
6225          * Fires when a mouseout occur
6226          * @param {Roo.bootstrap.Table} this
6227          * @param {Roo.Element} el
6228          * @param {Number} rowIndex
6229          * @param {Number} columnIndex
6230          * @param {Roo.EventObject} e
6231          */
6232         "mouseout" : true,
6233         /**
6234          * @event rowclass
6235          * Fires when a row is rendered, so you can change add a style to it.
6236          * @param {Roo.bootstrap.Table} this
6237          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6238          */
6239         'rowclass' : true,
6240           /**
6241          * @event rowsrendered
6242          * Fires when all the  rows have been rendered
6243          * @param {Roo.bootstrap.Table} this
6244          */
6245         'rowsrendered' : true,
6246         /**
6247          * @event contextmenu
6248          * The raw contextmenu event for the entire grid.
6249          * @param {Roo.EventObject} e
6250          */
6251         "contextmenu" : true,
6252         /**
6253          * @event rowcontextmenu
6254          * Fires when a row is right clicked
6255          * @param {Roo.bootstrap.Table} this
6256          * @param {Number} rowIndex
6257          * @param {Roo.EventObject} e
6258          */
6259         "rowcontextmenu" : true,
6260         /**
6261          * @event cellcontextmenu
6262          * Fires when a cell is right clicked
6263          * @param {Roo.bootstrap.Table} this
6264          * @param {Number} rowIndex
6265          * @param {Number} cellIndex
6266          * @param {Roo.EventObject} e
6267          */
6268          "cellcontextmenu" : true,
6269          /**
6270          * @event headercontextmenu
6271          * Fires when a header is right clicked
6272          * @param {Roo.bootstrap.Table} this
6273          * @param {Number} columnIndex
6274          * @param {Roo.EventObject} e
6275          */
6276         "headercontextmenu" : true
6277     });
6278 };
6279
6280 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6281     
6282     cls: false,
6283     align: false,
6284     bgcolor: false,
6285     border: false,
6286     cellpadding: false,
6287     cellspacing: false,
6288     frame: false,
6289     rules: false,
6290     sortable: false,
6291     summary: false,
6292     width: false,
6293     striped : false,
6294     scrollBody : false,
6295     bordered: false,
6296     hover:  false,
6297     condensed : false,
6298     responsive : false,
6299     sm : false,
6300     cm : false,
6301     store : false,
6302     loadMask : false,
6303     footerShow : true,
6304     headerShow : true,
6305   
6306     rowSelection : false,
6307     cellSelection : false,
6308     layout : false,
6309     
6310     // Roo.Element - the tbody
6311     mainBody: false,
6312     // Roo.Element - thead element
6313     mainHead: false,
6314     
6315     container: false, // used by gridpanel...
6316     
6317     lazyLoad : false,
6318     
6319     CSS : Roo.util.CSS,
6320     
6321     auto_hide_footer : false,
6322     
6323     getAutoCreate : function()
6324     {
6325         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6326         
6327         cfg = {
6328             tag: 'table',
6329             cls : 'table',
6330             cn : []
6331         };
6332         if (this.scrollBody) {
6333             cfg.cls += ' table-body-fixed';
6334         }    
6335         if (this.striped) {
6336             cfg.cls += ' table-striped';
6337         }
6338         
6339         if (this.hover) {
6340             cfg.cls += ' table-hover';
6341         }
6342         if (this.bordered) {
6343             cfg.cls += ' table-bordered';
6344         }
6345         if (this.condensed) {
6346             cfg.cls += ' table-condensed';
6347         }
6348         if (this.responsive) {
6349             cfg.cls += ' table-responsive';
6350         }
6351         
6352         if (this.cls) {
6353             cfg.cls+=  ' ' +this.cls;
6354         }
6355         
6356         // this lot should be simplifed...
6357         var _t = this;
6358         var cp = [
6359             'align',
6360             'bgcolor',
6361             'border',
6362             'cellpadding',
6363             'cellspacing',
6364             'frame',
6365             'rules',
6366             'sortable',
6367             'summary',
6368             'width'
6369         ].forEach(function(k) {
6370             if (_t[k]) {
6371                 cfg[k] = _t[k];
6372             }
6373         });
6374         
6375         
6376         if (this.layout) {
6377             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6378         }
6379         
6380         if(this.store || this.cm){
6381             if(this.headerShow){
6382                 cfg.cn.push(this.renderHeader());
6383             }
6384             
6385             cfg.cn.push(this.renderBody());
6386             
6387             if(this.footerShow){
6388                 cfg.cn.push(this.renderFooter());
6389             }
6390             // where does this come from?
6391             //cfg.cls+=  ' TableGrid';
6392         }
6393         
6394         return { cn : [ cfg ] };
6395     },
6396     
6397     initEvents : function()
6398     {   
6399         if(!this.store || !this.cm){
6400             return;
6401         }
6402         if (this.selModel) {
6403             this.selModel.initEvents();
6404         }
6405         
6406         
6407         //Roo.log('initEvents with ds!!!!');
6408         
6409         this.mainBody = this.el.select('tbody', true).first();
6410         this.mainHead = this.el.select('thead', true).first();
6411         this.mainFoot = this.el.select('tfoot', true).first();
6412         
6413         
6414         
6415         var _this = this;
6416         
6417         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6418             e.on('click', _this.sort, _this);
6419         });
6420         
6421         this.mainBody.on("click", this.onClick, this);
6422         this.mainBody.on("dblclick", this.onDblClick, this);
6423         
6424         // why is this done????? = it breaks dialogs??
6425         //this.parent().el.setStyle('position', 'relative');
6426         
6427         
6428         if (this.footer) {
6429             this.footer.parentId = this.id;
6430             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6431             
6432             if(this.lazyLoad){
6433                 this.el.select('tfoot tr td').first().addClass('hide');
6434             }
6435         } 
6436         
6437         if(this.loadMask) {
6438             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6439         }
6440         
6441         this.store.on('load', this.onLoad, this);
6442         this.store.on('beforeload', this.onBeforeLoad, this);
6443         this.store.on('update', this.onUpdate, this);
6444         this.store.on('add', this.onAdd, this);
6445         this.store.on("clear", this.clear, this);
6446         
6447         this.el.on("contextmenu", this.onContextMenu, this);
6448         
6449         this.mainBody.on('scroll', this.onBodyScroll, this);
6450         
6451         this.cm.on("headerchange", this.onHeaderChange, this);
6452         
6453         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6454         
6455     },
6456     
6457     onContextMenu : function(e, t)
6458     {
6459         this.processEvent("contextmenu", e);
6460     },
6461     
6462     processEvent : function(name, e)
6463     {
6464         if (name != 'touchstart' ) {
6465             this.fireEvent(name, e);    
6466         }
6467         
6468         var t = e.getTarget();
6469         
6470         var cell = Roo.get(t);
6471         
6472         if(!cell){
6473             return;
6474         }
6475         
6476         if(cell.findParent('tfoot', false, true)){
6477             return;
6478         }
6479         
6480         if(cell.findParent('thead', false, true)){
6481             
6482             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6483                 cell = Roo.get(t).findParent('th', false, true);
6484                 if (!cell) {
6485                     Roo.log("failed to find th in thead?");
6486                     Roo.log(e.getTarget());
6487                     return;
6488                 }
6489             }
6490             
6491             var cellIndex = cell.dom.cellIndex;
6492             
6493             var ename = name == 'touchstart' ? 'click' : name;
6494             this.fireEvent("header" + ename, this, cellIndex, e);
6495             
6496             return;
6497         }
6498         
6499         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6500             cell = Roo.get(t).findParent('td', false, true);
6501             if (!cell) {
6502                 Roo.log("failed to find th in tbody?");
6503                 Roo.log(e.getTarget());
6504                 return;
6505             }
6506         }
6507         
6508         var row = cell.findParent('tr', false, true);
6509         var cellIndex = cell.dom.cellIndex;
6510         var rowIndex = row.dom.rowIndex - 1;
6511         
6512         if(row !== false){
6513             
6514             this.fireEvent("row" + name, this, rowIndex, e);
6515             
6516             if(cell !== false){
6517             
6518                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6519             }
6520         }
6521         
6522     },
6523     
6524     onMouseover : function(e, el)
6525     {
6526         var cell = Roo.get(el);
6527         
6528         if(!cell){
6529             return;
6530         }
6531         
6532         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6533             cell = cell.findParent('td', false, true);
6534         }
6535         
6536         var row = cell.findParent('tr', false, true);
6537         var cellIndex = cell.dom.cellIndex;
6538         var rowIndex = row.dom.rowIndex - 1; // start from 0
6539         
6540         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6541         
6542     },
6543     
6544     onMouseout : function(e, el)
6545     {
6546         var cell = Roo.get(el);
6547         
6548         if(!cell){
6549             return;
6550         }
6551         
6552         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6553             cell = cell.findParent('td', false, true);
6554         }
6555         
6556         var row = cell.findParent('tr', false, true);
6557         var cellIndex = cell.dom.cellIndex;
6558         var rowIndex = row.dom.rowIndex - 1; // start from 0
6559         
6560         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6561         
6562     },
6563     
6564     onClick : function(e, el)
6565     {
6566         var cell = Roo.get(el);
6567         
6568         if(!cell || (!this.cellSelection && !this.rowSelection)){
6569             return;
6570         }
6571         
6572         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6573             cell = cell.findParent('td', false, true);
6574         }
6575         
6576         if(!cell || typeof(cell) == 'undefined'){
6577             return;
6578         }
6579         
6580         var row = cell.findParent('tr', false, true);
6581         
6582         if(!row || typeof(row) == 'undefined'){
6583             return;
6584         }
6585         
6586         var cellIndex = cell.dom.cellIndex;
6587         var rowIndex = this.getRowIndex(row);
6588         
6589         // why??? - should these not be based on SelectionModel?
6590         if(this.cellSelection){
6591             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6592         }
6593         
6594         if(this.rowSelection){
6595             this.fireEvent('rowclick', this, row, rowIndex, e);
6596         }
6597         
6598         
6599     },
6600         
6601     onDblClick : function(e,el)
6602     {
6603         var cell = Roo.get(el);
6604         
6605         if(!cell || (!this.cellSelection && !this.rowSelection)){
6606             return;
6607         }
6608         
6609         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6610             cell = cell.findParent('td', false, true);
6611         }
6612         
6613         if(!cell || typeof(cell) == 'undefined'){
6614             return;
6615         }
6616         
6617         var row = cell.findParent('tr', false, true);
6618         
6619         if(!row || typeof(row) == 'undefined'){
6620             return;
6621         }
6622         
6623         var cellIndex = cell.dom.cellIndex;
6624         var rowIndex = this.getRowIndex(row);
6625         
6626         if(this.cellSelection){
6627             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6628         }
6629         
6630         if(this.rowSelection){
6631             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6632         }
6633     },
6634     
6635     sort : function(e,el)
6636     {
6637         var col = Roo.get(el);
6638         
6639         if(!col.hasClass('sortable')){
6640             return;
6641         }
6642         
6643         var sort = col.attr('sort');
6644         var dir = 'ASC';
6645         
6646         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6647             dir = 'DESC';
6648         }
6649         
6650         this.store.sortInfo = {field : sort, direction : dir};
6651         
6652         if (this.footer) {
6653             Roo.log("calling footer first");
6654             this.footer.onClick('first');
6655         } else {
6656         
6657             this.store.load({ params : { start : 0 } });
6658         }
6659     },
6660     
6661     renderHeader : function()
6662     {
6663         var header = {
6664             tag: 'thead',
6665             cn : []
6666         };
6667         
6668         var cm = this.cm;
6669         this.totalWidth = 0;
6670         
6671         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6672             
6673             var config = cm.config[i];
6674             
6675             var c = {
6676                 tag: 'th',
6677                 cls : 'x-hcol-' + i,
6678                 style : '',
6679                 html: cm.getColumnHeader(i)
6680             };
6681             
6682             var hh = '';
6683             
6684             if(typeof(config.sortable) != 'undefined' && config.sortable){
6685                 c.cls = 'sortable';
6686                 c.html = '<i class="glyphicon"></i>' + c.html;
6687             }
6688             
6689             if(typeof(config.lgHeader) != 'undefined'){
6690                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6691             }
6692             
6693             if(typeof(config.mdHeader) != 'undefined'){
6694                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6695             }
6696             
6697             if(typeof(config.smHeader) != 'undefined'){
6698                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6699             }
6700             
6701             if(typeof(config.xsHeader) != 'undefined'){
6702                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6703             }
6704             
6705             if(hh.length){
6706                 c.html = hh;
6707             }
6708             
6709             if(typeof(config.tooltip) != 'undefined'){
6710                 c.tooltip = config.tooltip;
6711             }
6712             
6713             if(typeof(config.colspan) != 'undefined'){
6714                 c.colspan = config.colspan;
6715             }
6716             
6717             if(typeof(config.hidden) != 'undefined' && config.hidden){
6718                 c.style += ' display:none;';
6719             }
6720             
6721             if(typeof(config.dataIndex) != 'undefined'){
6722                 c.sort = config.dataIndex;
6723             }
6724             
6725            
6726             
6727             if(typeof(config.align) != 'undefined' && config.align.length){
6728                 c.style += ' text-align:' + config.align + ';';
6729             }
6730             
6731             if(typeof(config.width) != 'undefined'){
6732                 c.style += ' width:' + config.width + 'px;';
6733                 this.totalWidth += config.width;
6734             } else {
6735                 this.totalWidth += 100; // assume minimum of 100 per column?
6736             }
6737             
6738             if(typeof(config.cls) != 'undefined'){
6739                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6740             }
6741             
6742             ['xs','sm','md','lg'].map(function(size){
6743                 
6744                 if(typeof(config[size]) == 'undefined'){
6745                     return;
6746                 }
6747                 
6748                 if (!config[size]) { // 0 = hidden
6749                     c.cls += ' hidden-' + size;
6750                     return;
6751                 }
6752                 
6753                 c.cls += ' col-' + size + '-' + config[size];
6754
6755             });
6756             
6757             header.cn.push(c)
6758         }
6759         
6760         return header;
6761     },
6762     
6763     renderBody : function()
6764     {
6765         var body = {
6766             tag: 'tbody',
6767             cn : [
6768                 {
6769                     tag: 'tr',
6770                     cn : [
6771                         {
6772                             tag : 'td',
6773                             colspan :  this.cm.getColumnCount()
6774                         }
6775                     ]
6776                 }
6777             ]
6778         };
6779         
6780         return body;
6781     },
6782     
6783     renderFooter : function()
6784     {
6785         var footer = {
6786             tag: 'tfoot',
6787             cn : [
6788                 {
6789                     tag: 'tr',
6790                     cn : [
6791                         {
6792                             tag : 'td',
6793                             colspan :  this.cm.getColumnCount()
6794                         }
6795                     ]
6796                 }
6797             ]
6798         };
6799         
6800         return footer;
6801     },
6802     
6803     
6804     
6805     onLoad : function()
6806     {
6807 //        Roo.log('ds onload');
6808         this.clear();
6809         
6810         var _this = this;
6811         var cm = this.cm;
6812         var ds = this.store;
6813         
6814         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6815             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6816             if (_this.store.sortInfo) {
6817                     
6818                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6819                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6820                 }
6821                 
6822                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6823                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6824                 }
6825             }
6826         });
6827         
6828         var tbody =  this.mainBody;
6829               
6830         if(ds.getCount() > 0){
6831             ds.data.each(function(d,rowIndex){
6832                 var row =  this.renderRow(cm, ds, rowIndex);
6833                 
6834                 tbody.createChild(row);
6835                 
6836                 var _this = this;
6837                 
6838                 if(row.cellObjects.length){
6839                     Roo.each(row.cellObjects, function(r){
6840                         _this.renderCellObject(r);
6841                     })
6842                 }
6843                 
6844             }, this);
6845         }
6846         
6847         var tfoot = this.el.select('tfoot', true).first();
6848         
6849         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6850             
6851             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6852             
6853             var total = this.ds.getTotalCount();
6854             
6855             if(this.footer.pageSize < total){
6856                 this.mainFoot.show();
6857             }
6858         }
6859         
6860         Roo.each(this.el.select('tbody td', true).elements, function(e){
6861             e.on('mouseover', _this.onMouseover, _this);
6862         });
6863         
6864         Roo.each(this.el.select('tbody td', true).elements, function(e){
6865             e.on('mouseout', _this.onMouseout, _this);
6866         });
6867         this.fireEvent('rowsrendered', this);
6868         
6869         this.autoSize();
6870     },
6871     
6872     
6873     onUpdate : function(ds,record)
6874     {
6875         this.refreshRow(record);
6876         this.autoSize();
6877     },
6878     
6879     onRemove : function(ds, record, index, isUpdate){
6880         if(isUpdate !== true){
6881             this.fireEvent("beforerowremoved", this, index, record);
6882         }
6883         var bt = this.mainBody.dom;
6884         
6885         var rows = this.el.select('tbody > tr', true).elements;
6886         
6887         if(typeof(rows[index]) != 'undefined'){
6888             bt.removeChild(rows[index].dom);
6889         }
6890         
6891 //        if(bt.rows[index]){
6892 //            bt.removeChild(bt.rows[index]);
6893 //        }
6894         
6895         if(isUpdate !== true){
6896             //this.stripeRows(index);
6897             //this.syncRowHeights(index, index);
6898             //this.layout();
6899             this.fireEvent("rowremoved", this, index, record);
6900         }
6901     },
6902     
6903     onAdd : function(ds, records, rowIndex)
6904     {
6905         //Roo.log('on Add called');
6906         // - note this does not handle multiple adding very well..
6907         var bt = this.mainBody.dom;
6908         for (var i =0 ; i < records.length;i++) {
6909             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6910             //Roo.log(records[i]);
6911             //Roo.log(this.store.getAt(rowIndex+i));
6912             this.insertRow(this.store, rowIndex + i, false);
6913             return;
6914         }
6915         
6916     },
6917     
6918     
6919     refreshRow : function(record){
6920         var ds = this.store, index;
6921         if(typeof record == 'number'){
6922             index = record;
6923             record = ds.getAt(index);
6924         }else{
6925             index = ds.indexOf(record);
6926         }
6927         this.insertRow(ds, index, true);
6928         this.autoSize();
6929         this.onRemove(ds, record, index+1, true);
6930         this.autoSize();
6931         //this.syncRowHeights(index, index);
6932         //this.layout();
6933         this.fireEvent("rowupdated", this, index, record);
6934     },
6935     
6936     insertRow : function(dm, rowIndex, isUpdate){
6937         
6938         if(!isUpdate){
6939             this.fireEvent("beforerowsinserted", this, rowIndex);
6940         }
6941             //var s = this.getScrollState();
6942         var row = this.renderRow(this.cm, this.store, rowIndex);
6943         // insert before rowIndex..
6944         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6945         
6946         var _this = this;
6947                 
6948         if(row.cellObjects.length){
6949             Roo.each(row.cellObjects, function(r){
6950                 _this.renderCellObject(r);
6951             })
6952         }
6953             
6954         if(!isUpdate){
6955             this.fireEvent("rowsinserted", this, rowIndex);
6956             //this.syncRowHeights(firstRow, lastRow);
6957             //this.stripeRows(firstRow);
6958             //this.layout();
6959         }
6960         
6961     },
6962     
6963     
6964     getRowDom : function(rowIndex)
6965     {
6966         var rows = this.el.select('tbody > tr', true).elements;
6967         
6968         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6969         
6970     },
6971     // returns the object tree for a tr..
6972   
6973     
6974     renderRow : function(cm, ds, rowIndex) 
6975     {
6976         var d = ds.getAt(rowIndex);
6977         
6978         var row = {
6979             tag : 'tr',
6980             cls : 'x-row-' + rowIndex,
6981             cn : []
6982         };
6983             
6984         var cellObjects = [];
6985         
6986         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6987             var config = cm.config[i];
6988             
6989             var renderer = cm.getRenderer(i);
6990             var value = '';
6991             var id = false;
6992             
6993             if(typeof(renderer) !== 'undefined'){
6994                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6995             }
6996             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6997             // and are rendered into the cells after the row is rendered - using the id for the element.
6998             
6999             if(typeof(value) === 'object'){
7000                 id = Roo.id();
7001                 cellObjects.push({
7002                     container : id,
7003                     cfg : value 
7004                 })
7005             }
7006             
7007             var rowcfg = {
7008                 record: d,
7009                 rowIndex : rowIndex,
7010                 colIndex : i,
7011                 rowClass : ''
7012             };
7013
7014             this.fireEvent('rowclass', this, rowcfg);
7015             
7016             var td = {
7017                 tag: 'td',
7018                 cls : rowcfg.rowClass + ' x-col-' + i,
7019                 style: '',
7020                 html: (typeof(value) === 'object') ? '' : value
7021             };
7022             
7023             if (id) {
7024                 td.id = id;
7025             }
7026             
7027             if(typeof(config.colspan) != 'undefined'){
7028                 td.colspan = config.colspan;
7029             }
7030             
7031             if(typeof(config.hidden) != 'undefined' && config.hidden){
7032                 td.style += ' display:none;';
7033             }
7034             
7035             if(typeof(config.align) != 'undefined' && config.align.length){
7036                 td.style += ' text-align:' + config.align + ';';
7037             }
7038             if(typeof(config.valign) != 'undefined' && config.valign.length){
7039                 td.style += ' vertical-align:' + config.valign + ';';
7040             }
7041             
7042             if(typeof(config.width) != 'undefined'){
7043                 td.style += ' width:' +  config.width + 'px;';
7044             }
7045             
7046             if(typeof(config.cursor) != 'undefined'){
7047                 td.style += ' cursor:' +  config.cursor + ';';
7048             }
7049             
7050             if(typeof(config.cls) != 'undefined'){
7051                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7052             }
7053             
7054             ['xs','sm','md','lg'].map(function(size){
7055                 
7056                 if(typeof(config[size]) == 'undefined'){
7057                     return;
7058                 }
7059                 
7060                 if (!config[size]) { // 0 = hidden
7061                     td.cls += ' hidden-' + size;
7062                     return;
7063                 }
7064                 
7065                 td.cls += ' col-' + size + '-' + config[size];
7066
7067             });
7068             
7069             row.cn.push(td);
7070            
7071         }
7072         
7073         row.cellObjects = cellObjects;
7074         
7075         return row;
7076           
7077     },
7078     
7079     
7080     
7081     onBeforeLoad : function()
7082     {
7083         
7084     },
7085      /**
7086      * Remove all rows
7087      */
7088     clear : function()
7089     {
7090         this.el.select('tbody', true).first().dom.innerHTML = '';
7091     },
7092     /**
7093      * Show or hide a row.
7094      * @param {Number} rowIndex to show or hide
7095      * @param {Boolean} state hide
7096      */
7097     setRowVisibility : function(rowIndex, state)
7098     {
7099         var bt = this.mainBody.dom;
7100         
7101         var rows = this.el.select('tbody > tr', true).elements;
7102         
7103         if(typeof(rows[rowIndex]) == 'undefined'){
7104             return;
7105         }
7106         rows[rowIndex].dom.style.display = state ? '' : 'none';
7107     },
7108     
7109     
7110     getSelectionModel : function(){
7111         if(!this.selModel){
7112             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7113         }
7114         return this.selModel;
7115     },
7116     /*
7117      * Render the Roo.bootstrap object from renderder
7118      */
7119     renderCellObject : function(r)
7120     {
7121         var _this = this;
7122         
7123         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7124         
7125         var t = r.cfg.render(r.container);
7126         
7127         if(r.cfg.cn){
7128             Roo.each(r.cfg.cn, function(c){
7129                 var child = {
7130                     container: t.getChildContainer(),
7131                     cfg: c
7132                 };
7133                 _this.renderCellObject(child);
7134             })
7135         }
7136     },
7137     
7138     getRowIndex : function(row)
7139     {
7140         var rowIndex = -1;
7141         
7142         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7143             if(el != row){
7144                 return;
7145             }
7146             
7147             rowIndex = index;
7148         });
7149         
7150         return rowIndex;
7151     },
7152      /**
7153      * Returns the grid's underlying element = used by panel.Grid
7154      * @return {Element} The element
7155      */
7156     getGridEl : function(){
7157         return this.el;
7158     },
7159      /**
7160      * Forces a resize - used by panel.Grid
7161      * @return {Element} The element
7162      */
7163     autoSize : function()
7164     {
7165         //var ctr = Roo.get(this.container.dom.parentElement);
7166         var ctr = Roo.get(this.el.dom);
7167         
7168         var thd = this.getGridEl().select('thead',true).first();
7169         var tbd = this.getGridEl().select('tbody', true).first();
7170         var tfd = this.getGridEl().select('tfoot', true).first();
7171         
7172         var cw = ctr.getWidth();
7173         
7174         if (tbd) {
7175             
7176             tbd.setSize(ctr.getWidth(),
7177                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7178             );
7179             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7180             cw -= barsize;
7181         }
7182         cw = Math.max(cw, this.totalWidth);
7183         this.getGridEl().select('tr',true).setWidth(cw);
7184         // resize 'expandable coloumn?
7185         
7186         return; // we doe not have a view in this design..
7187         
7188     },
7189     onBodyScroll: function()
7190     {
7191         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7192         if(this.mainHead){
7193             this.mainHead.setStyle({
7194                 'position' : 'relative',
7195                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7196             });
7197         }
7198         
7199         if(this.lazyLoad){
7200             
7201             var scrollHeight = this.mainBody.dom.scrollHeight;
7202             
7203             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7204             
7205             var height = this.mainBody.getHeight();
7206             
7207             if(scrollHeight - height == scrollTop) {
7208                 
7209                 var total = this.ds.getTotalCount();
7210                 
7211                 if(this.footer.cursor + this.footer.pageSize < total){
7212                     
7213                     this.footer.ds.load({
7214                         params : {
7215                             start : this.footer.cursor + this.footer.pageSize,
7216                             limit : this.footer.pageSize
7217                         },
7218                         add : true
7219                     });
7220                 }
7221             }
7222             
7223         }
7224     },
7225     
7226     onHeaderChange : function()
7227     {
7228         var header = this.renderHeader();
7229         var table = this.el.select('table', true).first();
7230         
7231         this.mainHead.remove();
7232         this.mainHead = table.createChild(header, this.mainBody, false);
7233     },
7234     
7235     onHiddenChange : function(colModel, colIndex, hidden)
7236     {
7237         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7238         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7239         
7240         this.CSS.updateRule(thSelector, "display", "");
7241         this.CSS.updateRule(tdSelector, "display", "");
7242         
7243         if(hidden){
7244             this.CSS.updateRule(thSelector, "display", "none");
7245             this.CSS.updateRule(tdSelector, "display", "none");
7246         }
7247         
7248         this.onHeaderChange();
7249         this.onLoad();
7250     },
7251     
7252     setColumnWidth: function(col_index, width)
7253     {
7254         // width = "md-2 xs-2..."
7255         if(!this.colModel.config[col_index]) {
7256             return;
7257         }
7258         
7259         var w = width.split(" ");
7260         
7261         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7262         
7263         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7264         
7265         
7266         for(var j = 0; j < w.length; j++) {
7267             
7268             if(!w[j]) {
7269                 continue;
7270             }
7271             
7272             var size_cls = w[j].split("-");
7273             
7274             if(!Number.isInteger(size_cls[1] * 1)) {
7275                 continue;
7276             }
7277             
7278             if(!this.colModel.config[col_index][size_cls[0]]) {
7279                 continue;
7280             }
7281             
7282             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7283                 continue;
7284             }
7285             
7286             h_row[0].classList.replace(
7287                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7288                 "col-"+size_cls[0]+"-"+size_cls[1]
7289             );
7290             
7291             for(var i = 0; i < rows.length; i++) {
7292                 
7293                 var size_cls = w[j].split("-");
7294                 
7295                 if(!Number.isInteger(size_cls[1] * 1)) {
7296                     continue;
7297                 }
7298                 
7299                 if(!this.colModel.config[col_index][size_cls[0]]) {
7300                     continue;
7301                 }
7302                 
7303                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7304                     continue;
7305                 }
7306                 
7307                 rows[i].classList.replace(
7308                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7309                     "col-"+size_cls[0]+"-"+size_cls[1]
7310                 );
7311             }
7312             
7313             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7314         }
7315     }
7316 });
7317
7318  
7319
7320  /*
7321  * - LGPL
7322  *
7323  * table cell
7324  * 
7325  */
7326
7327 /**
7328  * @class Roo.bootstrap.TableCell
7329  * @extends Roo.bootstrap.Component
7330  * Bootstrap TableCell class
7331  * @cfg {String} html cell contain text
7332  * @cfg {String} cls cell class
7333  * @cfg {String} tag cell tag (td|th) default td
7334  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7335  * @cfg {String} align Aligns the content in a cell
7336  * @cfg {String} axis Categorizes cells
7337  * @cfg {String} bgcolor Specifies the background color of a cell
7338  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7339  * @cfg {Number} colspan Specifies the number of columns a cell should span
7340  * @cfg {String} headers Specifies one or more header cells a cell is related to
7341  * @cfg {Number} height Sets the height of a cell
7342  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7343  * @cfg {Number} rowspan Sets the number of rows a cell should span
7344  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7345  * @cfg {String} valign Vertical aligns the content in a cell
7346  * @cfg {Number} width Specifies the width of a cell
7347  * 
7348  * @constructor
7349  * Create a new TableCell
7350  * @param {Object} config The config object
7351  */
7352
7353 Roo.bootstrap.TableCell = function(config){
7354     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7355 };
7356
7357 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7358     
7359     html: false,
7360     cls: false,
7361     tag: false,
7362     abbr: false,
7363     align: false,
7364     axis: false,
7365     bgcolor: false,
7366     charoff: false,
7367     colspan: false,
7368     headers: false,
7369     height: false,
7370     nowrap: false,
7371     rowspan: false,
7372     scope: false,
7373     valign: false,
7374     width: false,
7375     
7376     
7377     getAutoCreate : function(){
7378         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7379         
7380         cfg = {
7381             tag: 'td'
7382         };
7383         
7384         if(this.tag){
7385             cfg.tag = this.tag;
7386         }
7387         
7388         if (this.html) {
7389             cfg.html=this.html
7390         }
7391         if (this.cls) {
7392             cfg.cls=this.cls
7393         }
7394         if (this.abbr) {
7395             cfg.abbr=this.abbr
7396         }
7397         if (this.align) {
7398             cfg.align=this.align
7399         }
7400         if (this.axis) {
7401             cfg.axis=this.axis
7402         }
7403         if (this.bgcolor) {
7404             cfg.bgcolor=this.bgcolor
7405         }
7406         if (this.charoff) {
7407             cfg.charoff=this.charoff
7408         }
7409         if (this.colspan) {
7410             cfg.colspan=this.colspan
7411         }
7412         if (this.headers) {
7413             cfg.headers=this.headers
7414         }
7415         if (this.height) {
7416             cfg.height=this.height
7417         }
7418         if (this.nowrap) {
7419             cfg.nowrap=this.nowrap
7420         }
7421         if (this.rowspan) {
7422             cfg.rowspan=this.rowspan
7423         }
7424         if (this.scope) {
7425             cfg.scope=this.scope
7426         }
7427         if (this.valign) {
7428             cfg.valign=this.valign
7429         }
7430         if (this.width) {
7431             cfg.width=this.width
7432         }
7433         
7434         
7435         return cfg;
7436     }
7437    
7438 });
7439
7440  
7441
7442  /*
7443  * - LGPL
7444  *
7445  * table row
7446  * 
7447  */
7448
7449 /**
7450  * @class Roo.bootstrap.TableRow
7451  * @extends Roo.bootstrap.Component
7452  * Bootstrap TableRow class
7453  * @cfg {String} cls row class
7454  * @cfg {String} align Aligns the content in a table row
7455  * @cfg {String} bgcolor Specifies a background color for a table row
7456  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7457  * @cfg {String} valign Vertical aligns the content in a table row
7458  * 
7459  * @constructor
7460  * Create a new TableRow
7461  * @param {Object} config The config object
7462  */
7463
7464 Roo.bootstrap.TableRow = function(config){
7465     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7466 };
7467
7468 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7469     
7470     cls: false,
7471     align: false,
7472     bgcolor: false,
7473     charoff: false,
7474     valign: false,
7475     
7476     getAutoCreate : function(){
7477         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7478         
7479         cfg = {
7480             tag: 'tr'
7481         };
7482             
7483         if(this.cls){
7484             cfg.cls = this.cls;
7485         }
7486         if(this.align){
7487             cfg.align = this.align;
7488         }
7489         if(this.bgcolor){
7490             cfg.bgcolor = this.bgcolor;
7491         }
7492         if(this.charoff){
7493             cfg.charoff = this.charoff;
7494         }
7495         if(this.valign){
7496             cfg.valign = this.valign;
7497         }
7498         
7499         return cfg;
7500     }
7501    
7502 });
7503
7504  
7505
7506  /*
7507  * - LGPL
7508  *
7509  * table body
7510  * 
7511  */
7512
7513 /**
7514  * @class Roo.bootstrap.TableBody
7515  * @extends Roo.bootstrap.Component
7516  * Bootstrap TableBody class
7517  * @cfg {String} cls element class
7518  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7519  * @cfg {String} align Aligns the content inside the element
7520  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7521  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7522  * 
7523  * @constructor
7524  * Create a new TableBody
7525  * @param {Object} config The config object
7526  */
7527
7528 Roo.bootstrap.TableBody = function(config){
7529     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7530 };
7531
7532 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7533     
7534     cls: false,
7535     tag: false,
7536     align: false,
7537     charoff: false,
7538     valign: false,
7539     
7540     getAutoCreate : function(){
7541         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7542         
7543         cfg = {
7544             tag: 'tbody'
7545         };
7546             
7547         if (this.cls) {
7548             cfg.cls=this.cls
7549         }
7550         if(this.tag){
7551             cfg.tag = this.tag;
7552         }
7553         
7554         if(this.align){
7555             cfg.align = this.align;
7556         }
7557         if(this.charoff){
7558             cfg.charoff = this.charoff;
7559         }
7560         if(this.valign){
7561             cfg.valign = this.valign;
7562         }
7563         
7564         return cfg;
7565     }
7566     
7567     
7568 //    initEvents : function()
7569 //    {
7570 //        
7571 //        if(!this.store){
7572 //            return;
7573 //        }
7574 //        
7575 //        this.store = Roo.factory(this.store, Roo.data);
7576 //        this.store.on('load', this.onLoad, this);
7577 //        
7578 //        this.store.load();
7579 //        
7580 //    },
7581 //    
7582 //    onLoad: function () 
7583 //    {   
7584 //        this.fireEvent('load', this);
7585 //    }
7586 //    
7587 //   
7588 });
7589
7590  
7591
7592  /*
7593  * Based on:
7594  * Ext JS Library 1.1.1
7595  * Copyright(c) 2006-2007, Ext JS, LLC.
7596  *
7597  * Originally Released Under LGPL - original licence link has changed is not relivant.
7598  *
7599  * Fork - LGPL
7600  * <script type="text/javascript">
7601  */
7602
7603 // as we use this in bootstrap.
7604 Roo.namespace('Roo.form');
7605  /**
7606  * @class Roo.form.Action
7607  * Internal Class used to handle form actions
7608  * @constructor
7609  * @param {Roo.form.BasicForm} el The form element or its id
7610  * @param {Object} config Configuration options
7611  */
7612
7613  
7614  
7615 // define the action interface
7616 Roo.form.Action = function(form, options){
7617     this.form = form;
7618     this.options = options || {};
7619 };
7620 /**
7621  * Client Validation Failed
7622  * @const 
7623  */
7624 Roo.form.Action.CLIENT_INVALID = 'client';
7625 /**
7626  * Server Validation Failed
7627  * @const 
7628  */
7629 Roo.form.Action.SERVER_INVALID = 'server';
7630  /**
7631  * Connect to Server Failed
7632  * @const 
7633  */
7634 Roo.form.Action.CONNECT_FAILURE = 'connect';
7635 /**
7636  * Reading Data from Server Failed
7637  * @const 
7638  */
7639 Roo.form.Action.LOAD_FAILURE = 'load';
7640
7641 Roo.form.Action.prototype = {
7642     type : 'default',
7643     failureType : undefined,
7644     response : undefined,
7645     result : undefined,
7646
7647     // interface method
7648     run : function(options){
7649
7650     },
7651
7652     // interface method
7653     success : function(response){
7654
7655     },
7656
7657     // interface method
7658     handleResponse : function(response){
7659
7660     },
7661
7662     // default connection failure
7663     failure : function(response){
7664         
7665         this.response = response;
7666         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7667         this.form.afterAction(this, false);
7668     },
7669
7670     processResponse : function(response){
7671         this.response = response;
7672         if(!response.responseText){
7673             return true;
7674         }
7675         this.result = this.handleResponse(response);
7676         return this.result;
7677     },
7678
7679     // utility functions used internally
7680     getUrl : function(appendParams){
7681         var url = this.options.url || this.form.url || this.form.el.dom.action;
7682         if(appendParams){
7683             var p = this.getParams();
7684             if(p){
7685                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7686             }
7687         }
7688         return url;
7689     },
7690
7691     getMethod : function(){
7692         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7693     },
7694
7695     getParams : function(){
7696         var bp = this.form.baseParams;
7697         var p = this.options.params;
7698         if(p){
7699             if(typeof p == "object"){
7700                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7701             }else if(typeof p == 'string' && bp){
7702                 p += '&' + Roo.urlEncode(bp);
7703             }
7704         }else if(bp){
7705             p = Roo.urlEncode(bp);
7706         }
7707         return p;
7708     },
7709
7710     createCallback : function(){
7711         return {
7712             success: this.success,
7713             failure: this.failure,
7714             scope: this,
7715             timeout: (this.form.timeout*1000),
7716             upload: this.form.fileUpload ? this.success : undefined
7717         };
7718     }
7719 };
7720
7721 Roo.form.Action.Submit = function(form, options){
7722     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7723 };
7724
7725 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7726     type : 'submit',
7727
7728     haveProgress : false,
7729     uploadComplete : false,
7730     
7731     // uploadProgress indicator.
7732     uploadProgress : function()
7733     {
7734         if (!this.form.progressUrl) {
7735             return;
7736         }
7737         
7738         if (!this.haveProgress) {
7739             Roo.MessageBox.progress("Uploading", "Uploading");
7740         }
7741         if (this.uploadComplete) {
7742            Roo.MessageBox.hide();
7743            return;
7744         }
7745         
7746         this.haveProgress = true;
7747    
7748         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7749         
7750         var c = new Roo.data.Connection();
7751         c.request({
7752             url : this.form.progressUrl,
7753             params: {
7754                 id : uid
7755             },
7756             method: 'GET',
7757             success : function(req){
7758                //console.log(data);
7759                 var rdata = false;
7760                 var edata;
7761                 try  {
7762                    rdata = Roo.decode(req.responseText)
7763                 } catch (e) {
7764                     Roo.log("Invalid data from server..");
7765                     Roo.log(edata);
7766                     return;
7767                 }
7768                 if (!rdata || !rdata.success) {
7769                     Roo.log(rdata);
7770                     Roo.MessageBox.alert(Roo.encode(rdata));
7771                     return;
7772                 }
7773                 var data = rdata.data;
7774                 
7775                 if (this.uploadComplete) {
7776                    Roo.MessageBox.hide();
7777                    return;
7778                 }
7779                    
7780                 if (data){
7781                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7782                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7783                     );
7784                 }
7785                 this.uploadProgress.defer(2000,this);
7786             },
7787        
7788             failure: function(data) {
7789                 Roo.log('progress url failed ');
7790                 Roo.log(data);
7791             },
7792             scope : this
7793         });
7794            
7795     },
7796     
7797     
7798     run : function()
7799     {
7800         // run get Values on the form, so it syncs any secondary forms.
7801         this.form.getValues();
7802         
7803         var o = this.options;
7804         var method = this.getMethod();
7805         var isPost = method == 'POST';
7806         if(o.clientValidation === false || this.form.isValid()){
7807             
7808             if (this.form.progressUrl) {
7809                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7810                     (new Date() * 1) + '' + Math.random());
7811                     
7812             } 
7813             
7814             
7815             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7816                 form:this.form.el.dom,
7817                 url:this.getUrl(!isPost),
7818                 method: method,
7819                 params:isPost ? this.getParams() : null,
7820                 isUpload: this.form.fileUpload
7821             }));
7822             
7823             this.uploadProgress();
7824
7825         }else if (o.clientValidation !== false){ // client validation failed
7826             this.failureType = Roo.form.Action.CLIENT_INVALID;
7827             this.form.afterAction(this, false);
7828         }
7829     },
7830
7831     success : function(response)
7832     {
7833         this.uploadComplete= true;
7834         if (this.haveProgress) {
7835             Roo.MessageBox.hide();
7836         }
7837         
7838         
7839         var result = this.processResponse(response);
7840         if(result === true || result.success){
7841             this.form.afterAction(this, true);
7842             return;
7843         }
7844         if(result.errors){
7845             this.form.markInvalid(result.errors);
7846             this.failureType = Roo.form.Action.SERVER_INVALID;
7847         }
7848         this.form.afterAction(this, false);
7849     },
7850     failure : function(response)
7851     {
7852         this.uploadComplete= true;
7853         if (this.haveProgress) {
7854             Roo.MessageBox.hide();
7855         }
7856         
7857         this.response = response;
7858         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7859         this.form.afterAction(this, false);
7860     },
7861     
7862     handleResponse : function(response){
7863         if(this.form.errorReader){
7864             var rs = this.form.errorReader.read(response);
7865             var errors = [];
7866             if(rs.records){
7867                 for(var i = 0, len = rs.records.length; i < len; i++) {
7868                     var r = rs.records[i];
7869                     errors[i] = r.data;
7870                 }
7871             }
7872             if(errors.length < 1){
7873                 errors = null;
7874             }
7875             return {
7876                 success : rs.success,
7877                 errors : errors
7878             };
7879         }
7880         var ret = false;
7881         try {
7882             ret = Roo.decode(response.responseText);
7883         } catch (e) {
7884             ret = {
7885                 success: false,
7886                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7887                 errors : []
7888             };
7889         }
7890         return ret;
7891         
7892     }
7893 });
7894
7895
7896 Roo.form.Action.Load = function(form, options){
7897     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7898     this.reader = this.form.reader;
7899 };
7900
7901 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7902     type : 'load',
7903
7904     run : function(){
7905         
7906         Roo.Ajax.request(Roo.apply(
7907                 this.createCallback(), {
7908                     method:this.getMethod(),
7909                     url:this.getUrl(false),
7910                     params:this.getParams()
7911         }));
7912     },
7913
7914     success : function(response){
7915         
7916         var result = this.processResponse(response);
7917         if(result === true || !result.success || !result.data){
7918             this.failureType = Roo.form.Action.LOAD_FAILURE;
7919             this.form.afterAction(this, false);
7920             return;
7921         }
7922         this.form.clearInvalid();
7923         this.form.setValues(result.data);
7924         this.form.afterAction(this, true);
7925     },
7926
7927     handleResponse : function(response){
7928         if(this.form.reader){
7929             var rs = this.form.reader.read(response);
7930             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7931             return {
7932                 success : rs.success,
7933                 data : data
7934             };
7935         }
7936         return Roo.decode(response.responseText);
7937     }
7938 });
7939
7940 Roo.form.Action.ACTION_TYPES = {
7941     'load' : Roo.form.Action.Load,
7942     'submit' : Roo.form.Action.Submit
7943 };/*
7944  * - LGPL
7945  *
7946  * form
7947  *
7948  */
7949
7950 /**
7951  * @class Roo.bootstrap.Form
7952  * @extends Roo.bootstrap.Component
7953  * Bootstrap Form class
7954  * @cfg {String} method  GET | POST (default POST)
7955  * @cfg {String} labelAlign top | left (default top)
7956  * @cfg {String} align left  | right - for navbars
7957  * @cfg {Boolean} loadMask load mask when submit (default true)
7958
7959  *
7960  * @constructor
7961  * Create a new Form
7962  * @param {Object} config The config object
7963  */
7964
7965
7966 Roo.bootstrap.Form = function(config){
7967     
7968     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7969     
7970     Roo.bootstrap.Form.popover.apply();
7971     
7972     this.addEvents({
7973         /**
7974          * @event clientvalidation
7975          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7976          * @param {Form} this
7977          * @param {Boolean} valid true if the form has passed client-side validation
7978          */
7979         clientvalidation: true,
7980         /**
7981          * @event beforeaction
7982          * Fires before any action is performed. Return false to cancel the action.
7983          * @param {Form} this
7984          * @param {Action} action The action to be performed
7985          */
7986         beforeaction: true,
7987         /**
7988          * @event actionfailed
7989          * Fires when an action fails.
7990          * @param {Form} this
7991          * @param {Action} action The action that failed
7992          */
7993         actionfailed : true,
7994         /**
7995          * @event actioncomplete
7996          * Fires when an action is completed.
7997          * @param {Form} this
7998          * @param {Action} action The action that completed
7999          */
8000         actioncomplete : true
8001     });
8002 };
8003
8004 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8005
8006      /**
8007      * @cfg {String} method
8008      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8009      */
8010     method : 'POST',
8011     /**
8012      * @cfg {String} url
8013      * The URL to use for form actions if one isn't supplied in the action options.
8014      */
8015     /**
8016      * @cfg {Boolean} fileUpload
8017      * Set to true if this form is a file upload.
8018      */
8019
8020     /**
8021      * @cfg {Object} baseParams
8022      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8023      */
8024
8025     /**
8026      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8027      */
8028     timeout: 30,
8029     /**
8030      * @cfg {Sting} align (left|right) for navbar forms
8031      */
8032     align : 'left',
8033
8034     // private
8035     activeAction : null,
8036
8037     /**
8038      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8039      * element by passing it or its id or mask the form itself by passing in true.
8040      * @type Mixed
8041      */
8042     waitMsgTarget : false,
8043
8044     loadMask : true,
8045     
8046     /**
8047      * @cfg {Boolean} errorMask (true|false) default false
8048      */
8049     errorMask : false,
8050     
8051     /**
8052      * @cfg {Number} maskOffset Default 100
8053      */
8054     maskOffset : 100,
8055     
8056     /**
8057      * @cfg {Boolean} maskBody
8058      */
8059     maskBody : false,
8060
8061     getAutoCreate : function(){
8062
8063         var cfg = {
8064             tag: 'form',
8065             method : this.method || 'POST',
8066             id : this.id || Roo.id(),
8067             cls : ''
8068         };
8069         if (this.parent().xtype.match(/^Nav/)) {
8070             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8071
8072         }
8073
8074         if (this.labelAlign == 'left' ) {
8075             cfg.cls += ' form-horizontal';
8076         }
8077
8078
8079         return cfg;
8080     },
8081     initEvents : function()
8082     {
8083         this.el.on('submit', this.onSubmit, this);
8084         // this was added as random key presses on the form where triggering form submit.
8085         this.el.on('keypress', function(e) {
8086             if (e.getCharCode() != 13) {
8087                 return true;
8088             }
8089             // we might need to allow it for textareas.. and some other items.
8090             // check e.getTarget().
8091
8092             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8093                 return true;
8094             }
8095
8096             Roo.log("keypress blocked");
8097
8098             e.preventDefault();
8099             return false;
8100         });
8101         
8102     },
8103     // private
8104     onSubmit : function(e){
8105         e.stopEvent();
8106     },
8107
8108      /**
8109      * Returns true if client-side validation on the form is successful.
8110      * @return Boolean
8111      */
8112     isValid : function(){
8113         var items = this.getItems();
8114         var valid = true;
8115         var target = false;
8116         
8117         items.each(function(f){
8118             
8119             if(f.validate()){
8120                 return;
8121             }
8122             
8123             Roo.log('invalid field: ' + f.name);
8124             
8125             valid = false;
8126
8127             if(!target && f.el.isVisible(true)){
8128                 target = f;
8129             }
8130            
8131         });
8132         
8133         if(this.errorMask && !valid){
8134             Roo.bootstrap.Form.popover.mask(this, target);
8135         }
8136         
8137         return valid;
8138     },
8139     
8140     /**
8141      * Returns true if any fields in this form have changed since their original load.
8142      * @return Boolean
8143      */
8144     isDirty : function(){
8145         var dirty = false;
8146         var items = this.getItems();
8147         items.each(function(f){
8148            if(f.isDirty()){
8149                dirty = true;
8150                return false;
8151            }
8152            return true;
8153         });
8154         return dirty;
8155     },
8156      /**
8157      * Performs a predefined action (submit or load) or custom actions you define on this form.
8158      * @param {String} actionName The name of the action type
8159      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8160      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8161      * accept other config options):
8162      * <pre>
8163 Property          Type             Description
8164 ----------------  ---------------  ----------------------------------------------------------------------------------
8165 url               String           The url for the action (defaults to the form's url)
8166 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8167 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8168 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8169                                    validate the form on the client (defaults to false)
8170      * </pre>
8171      * @return {BasicForm} this
8172      */
8173     doAction : function(action, options){
8174         if(typeof action == 'string'){
8175             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8176         }
8177         if(this.fireEvent('beforeaction', this, action) !== false){
8178             this.beforeAction(action);
8179             action.run.defer(100, action);
8180         }
8181         return this;
8182     },
8183
8184     // private
8185     beforeAction : function(action){
8186         var o = action.options;
8187         
8188         if(this.loadMask){
8189             
8190             if(this.maskBody){
8191                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8192             } else {
8193                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8194             }
8195         }
8196         // not really supported yet.. ??
8197
8198         //if(this.waitMsgTarget === true){
8199         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8200         //}else if(this.waitMsgTarget){
8201         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8202         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8203         //}else {
8204         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8205        // }
8206
8207     },
8208
8209     // private
8210     afterAction : function(action, success){
8211         this.activeAction = null;
8212         var o = action.options;
8213
8214         if(this.loadMask){
8215             
8216             if(this.maskBody){
8217                 Roo.get(document.body).unmask();
8218             } else {
8219                 this.el.unmask();
8220             }
8221         }
8222         
8223         //if(this.waitMsgTarget === true){
8224 //            this.el.unmask();
8225         //}else if(this.waitMsgTarget){
8226         //    this.waitMsgTarget.unmask();
8227         //}else{
8228         //    Roo.MessageBox.updateProgress(1);
8229         //    Roo.MessageBox.hide();
8230        // }
8231         //
8232         if(success){
8233             if(o.reset){
8234                 this.reset();
8235             }
8236             Roo.callback(o.success, o.scope, [this, action]);
8237             this.fireEvent('actioncomplete', this, action);
8238
8239         }else{
8240
8241             // failure condition..
8242             // we have a scenario where updates need confirming.
8243             // eg. if a locking scenario exists..
8244             // we look for { errors : { needs_confirm : true }} in the response.
8245             if (
8246                 (typeof(action.result) != 'undefined')  &&
8247                 (typeof(action.result.errors) != 'undefined')  &&
8248                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8249            ){
8250                 var _t = this;
8251                 Roo.log("not supported yet");
8252                  /*
8253
8254                 Roo.MessageBox.confirm(
8255                     "Change requires confirmation",
8256                     action.result.errorMsg,
8257                     function(r) {
8258                         if (r != 'yes') {
8259                             return;
8260                         }
8261                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8262                     }
8263
8264                 );
8265                 */
8266
8267
8268                 return;
8269             }
8270
8271             Roo.callback(o.failure, o.scope, [this, action]);
8272             // show an error message if no failed handler is set..
8273             if (!this.hasListener('actionfailed')) {
8274                 Roo.log("need to add dialog support");
8275                 /*
8276                 Roo.MessageBox.alert("Error",
8277                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8278                         action.result.errorMsg :
8279                         "Saving Failed, please check your entries or try again"
8280                 );
8281                 */
8282             }
8283
8284             this.fireEvent('actionfailed', this, action);
8285         }
8286
8287     },
8288     /**
8289      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8290      * @param {String} id The value to search for
8291      * @return Field
8292      */
8293     findField : function(id){
8294         var items = this.getItems();
8295         var field = items.get(id);
8296         if(!field){
8297              items.each(function(f){
8298                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8299                     field = f;
8300                     return false;
8301                 }
8302                 return true;
8303             });
8304         }
8305         return field || null;
8306     },
8307      /**
8308      * Mark fields in this form invalid in bulk.
8309      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8310      * @return {BasicForm} this
8311      */
8312     markInvalid : function(errors){
8313         if(errors instanceof Array){
8314             for(var i = 0, len = errors.length; i < len; i++){
8315                 var fieldError = errors[i];
8316                 var f = this.findField(fieldError.id);
8317                 if(f){
8318                     f.markInvalid(fieldError.msg);
8319                 }
8320             }
8321         }else{
8322             var field, id;
8323             for(id in errors){
8324                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8325                     field.markInvalid(errors[id]);
8326                 }
8327             }
8328         }
8329         //Roo.each(this.childForms || [], function (f) {
8330         //    f.markInvalid(errors);
8331         //});
8332
8333         return this;
8334     },
8335
8336     /**
8337      * Set values for fields in this form in bulk.
8338      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8339      * @return {BasicForm} this
8340      */
8341     setValues : function(values){
8342         if(values instanceof Array){ // array of objects
8343             for(var i = 0, len = values.length; i < len; i++){
8344                 var v = values[i];
8345                 var f = this.findField(v.id);
8346                 if(f){
8347                     f.setValue(v.value);
8348                     if(this.trackResetOnLoad){
8349                         f.originalValue = f.getValue();
8350                     }
8351                 }
8352             }
8353         }else{ // object hash
8354             var field, id;
8355             for(id in values){
8356                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8357
8358                     if (field.setFromData &&
8359                         field.valueField &&
8360                         field.displayField &&
8361                         // combos' with local stores can
8362                         // be queried via setValue()
8363                         // to set their value..
8364                         (field.store && !field.store.isLocal)
8365                         ) {
8366                         // it's a combo
8367                         var sd = { };
8368                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8369                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8370                         field.setFromData(sd);
8371
8372                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8373                         
8374                         field.setFromData(values);
8375                         
8376                     } else {
8377                         field.setValue(values[id]);
8378                     }
8379
8380
8381                     if(this.trackResetOnLoad){
8382                         field.originalValue = field.getValue();
8383                     }
8384                 }
8385             }
8386         }
8387
8388         //Roo.each(this.childForms || [], function (f) {
8389         //    f.setValues(values);
8390         //});
8391
8392         return this;
8393     },
8394
8395     /**
8396      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8397      * they are returned as an array.
8398      * @param {Boolean} asString
8399      * @return {Object}
8400      */
8401     getValues : function(asString){
8402         //if (this.childForms) {
8403             // copy values from the child forms
8404         //    Roo.each(this.childForms, function (f) {
8405         //        this.setValues(f.getValues());
8406         //    }, this);
8407         //}
8408
8409
8410
8411         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8412         if(asString === true){
8413             return fs;
8414         }
8415         return Roo.urlDecode(fs);
8416     },
8417
8418     /**
8419      * Returns the fields in this form as an object with key/value pairs.
8420      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8421      * @return {Object}
8422      */
8423     getFieldValues : function(with_hidden)
8424     {
8425         var items = this.getItems();
8426         var ret = {};
8427         items.each(function(f){
8428             
8429             if (!f.getName()) {
8430                 return;
8431             }
8432             
8433             var v = f.getValue();
8434             
8435             if (f.inputType =='radio') {
8436                 if (typeof(ret[f.getName()]) == 'undefined') {
8437                     ret[f.getName()] = ''; // empty..
8438                 }
8439
8440                 if (!f.el.dom.checked) {
8441                     return;
8442
8443                 }
8444                 v = f.el.dom.value;
8445
8446             }
8447             
8448             if(f.xtype == 'MoneyField'){
8449                 ret[f.currencyName] = f.getCurrency();
8450             }
8451
8452             // not sure if this supported any more..
8453             if ((typeof(v) == 'object') && f.getRawValue) {
8454                 v = f.getRawValue() ; // dates..
8455             }
8456             // combo boxes where name != hiddenName...
8457             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8458                 ret[f.name] = f.getRawValue();
8459             }
8460             ret[f.getName()] = v;
8461         });
8462
8463         return ret;
8464     },
8465
8466     /**
8467      * Clears all invalid messages in this form.
8468      * @return {BasicForm} this
8469      */
8470     clearInvalid : function(){
8471         var items = this.getItems();
8472
8473         items.each(function(f){
8474            f.clearInvalid();
8475         });
8476
8477         return this;
8478     },
8479
8480     /**
8481      * Resets this form.
8482      * @return {BasicForm} this
8483      */
8484     reset : function(){
8485         var items = this.getItems();
8486         items.each(function(f){
8487             f.reset();
8488         });
8489
8490         Roo.each(this.childForms || [], function (f) {
8491             f.reset();
8492         });
8493
8494
8495         return this;
8496     },
8497     
8498     getItems : function()
8499     {
8500         var r=new Roo.util.MixedCollection(false, function(o){
8501             return o.id || (o.id = Roo.id());
8502         });
8503         var iter = function(el) {
8504             if (el.inputEl) {
8505                 r.add(el);
8506             }
8507             if (!el.items) {
8508                 return;
8509             }
8510             Roo.each(el.items,function(e) {
8511                 iter(e);
8512             });
8513         };
8514
8515         iter(this);
8516         return r;
8517     },
8518     
8519     hideFields : function(items)
8520     {
8521         Roo.each(items, function(i){
8522             
8523             var f = this.findField(i);
8524             
8525             if(!f){
8526                 return;
8527             }
8528             
8529             f.hide();
8530             
8531         }, this);
8532     },
8533     
8534     showFields : function(items)
8535     {
8536         Roo.each(items, function(i){
8537             
8538             var f = this.findField(i);
8539             
8540             if(!f){
8541                 return;
8542             }
8543             
8544             f.show();
8545             
8546         }, this);
8547     }
8548
8549 });
8550
8551 Roo.apply(Roo.bootstrap.Form, {
8552     
8553     popover : {
8554         
8555         padding : 5,
8556         
8557         isApplied : false,
8558         
8559         isMasked : false,
8560         
8561         form : false,
8562         
8563         target : false,
8564         
8565         toolTip : false,
8566         
8567         intervalID : false,
8568         
8569         maskEl : false,
8570         
8571         apply : function()
8572         {
8573             if(this.isApplied){
8574                 return;
8575             }
8576             
8577             this.maskEl = {
8578                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8579                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8580                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8581                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8582             };
8583             
8584             this.maskEl.top.enableDisplayMode("block");
8585             this.maskEl.left.enableDisplayMode("block");
8586             this.maskEl.bottom.enableDisplayMode("block");
8587             this.maskEl.right.enableDisplayMode("block");
8588             
8589             this.toolTip = new Roo.bootstrap.Tooltip({
8590                 cls : 'roo-form-error-popover',
8591                 alignment : {
8592                     'left' : ['r-l', [-2,0], 'right'],
8593                     'right' : ['l-r', [2,0], 'left'],
8594                     'bottom' : ['tl-bl', [0,2], 'top'],
8595                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8596                 }
8597             });
8598             
8599             this.toolTip.render(Roo.get(document.body));
8600
8601             this.toolTip.el.enableDisplayMode("block");
8602             
8603             Roo.get(document.body).on('click', function(){
8604                 this.unmask();
8605             }, this);
8606             
8607             Roo.get(document.body).on('touchstart', function(){
8608                 this.unmask();
8609             }, this);
8610             
8611             this.isApplied = true
8612         },
8613         
8614         mask : function(form, target)
8615         {
8616             this.form = form;
8617             
8618             this.target = target;
8619             
8620             if(!this.form.errorMask || !target.el){
8621                 return;
8622             }
8623             
8624             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8625             
8626             Roo.log(scrollable);
8627             
8628             var ot = this.target.el.calcOffsetsTo(scrollable);
8629             
8630             var scrollTo = ot[1] - this.form.maskOffset;
8631             
8632             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8633             
8634             scrollable.scrollTo('top', scrollTo);
8635             
8636             var box = this.target.el.getBox();
8637             Roo.log(box);
8638             var zIndex = Roo.bootstrap.Modal.zIndex++;
8639
8640             
8641             this.maskEl.top.setStyle('position', 'absolute');
8642             this.maskEl.top.setStyle('z-index', zIndex);
8643             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8644             this.maskEl.top.setLeft(0);
8645             this.maskEl.top.setTop(0);
8646             this.maskEl.top.show();
8647             
8648             this.maskEl.left.setStyle('position', 'absolute');
8649             this.maskEl.left.setStyle('z-index', zIndex);
8650             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8651             this.maskEl.left.setLeft(0);
8652             this.maskEl.left.setTop(box.y - this.padding);
8653             this.maskEl.left.show();
8654
8655             this.maskEl.bottom.setStyle('position', 'absolute');
8656             this.maskEl.bottom.setStyle('z-index', zIndex);
8657             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8658             this.maskEl.bottom.setLeft(0);
8659             this.maskEl.bottom.setTop(box.bottom + this.padding);
8660             this.maskEl.bottom.show();
8661
8662             this.maskEl.right.setStyle('position', 'absolute');
8663             this.maskEl.right.setStyle('z-index', zIndex);
8664             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8665             this.maskEl.right.setLeft(box.right + this.padding);
8666             this.maskEl.right.setTop(box.y - this.padding);
8667             this.maskEl.right.show();
8668
8669             this.toolTip.bindEl = this.target.el;
8670
8671             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8672
8673             var tip = this.target.blankText;
8674
8675             if(this.target.getValue() !== '' ) {
8676                 
8677                 if (this.target.invalidText.length) {
8678                     tip = this.target.invalidText;
8679                 } else if (this.target.regexText.length){
8680                     tip = this.target.regexText;
8681                 }
8682             }
8683
8684             this.toolTip.show(tip);
8685
8686             this.intervalID = window.setInterval(function() {
8687                 Roo.bootstrap.Form.popover.unmask();
8688             }, 10000);
8689
8690             window.onwheel = function(){ return false;};
8691             
8692             (function(){ this.isMasked = true; }).defer(500, this);
8693             
8694         },
8695         
8696         unmask : function()
8697         {
8698             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8699                 return;
8700             }
8701             
8702             this.maskEl.top.setStyle('position', 'absolute');
8703             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8704             this.maskEl.top.hide();
8705
8706             this.maskEl.left.setStyle('position', 'absolute');
8707             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8708             this.maskEl.left.hide();
8709
8710             this.maskEl.bottom.setStyle('position', 'absolute');
8711             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8712             this.maskEl.bottom.hide();
8713
8714             this.maskEl.right.setStyle('position', 'absolute');
8715             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8716             this.maskEl.right.hide();
8717             
8718             this.toolTip.hide();
8719             
8720             this.toolTip.el.hide();
8721             
8722             window.onwheel = function(){ return true;};
8723             
8724             if(this.intervalID){
8725                 window.clearInterval(this.intervalID);
8726                 this.intervalID = false;
8727             }
8728             
8729             this.isMasked = false;
8730             
8731         }
8732         
8733     }
8734     
8735 });
8736
8737 /*
8738  * Based on:
8739  * Ext JS Library 1.1.1
8740  * Copyright(c) 2006-2007, Ext JS, LLC.
8741  *
8742  * Originally Released Under LGPL - original licence link has changed is not relivant.
8743  *
8744  * Fork - LGPL
8745  * <script type="text/javascript">
8746  */
8747 /**
8748  * @class Roo.form.VTypes
8749  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8750  * @singleton
8751  */
8752 Roo.form.VTypes = function(){
8753     // closure these in so they are only created once.
8754     var alpha = /^[a-zA-Z_]+$/;
8755     var alphanum = /^[a-zA-Z0-9_]+$/;
8756     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8757     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8758
8759     // All these messages and functions are configurable
8760     return {
8761         /**
8762          * The function used to validate email addresses
8763          * @param {String} value The email address
8764          */
8765         'email' : function(v){
8766             return email.test(v);
8767         },
8768         /**
8769          * The error text to display when the email validation function returns false
8770          * @type String
8771          */
8772         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8773         /**
8774          * The keystroke filter mask to be applied on email input
8775          * @type RegExp
8776          */
8777         'emailMask' : /[a-z0-9_\.\-@]/i,
8778
8779         /**
8780          * The function used to validate URLs
8781          * @param {String} value The URL
8782          */
8783         'url' : function(v){
8784             return url.test(v);
8785         },
8786         /**
8787          * The error text to display when the url validation function returns false
8788          * @type String
8789          */
8790         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8791         
8792         /**
8793          * The function used to validate alpha values
8794          * @param {String} value The value
8795          */
8796         'alpha' : function(v){
8797             return alpha.test(v);
8798         },
8799         /**
8800          * The error text to display when the alpha validation function returns false
8801          * @type String
8802          */
8803         'alphaText' : 'This field should only contain letters and _',
8804         /**
8805          * The keystroke filter mask to be applied on alpha input
8806          * @type RegExp
8807          */
8808         'alphaMask' : /[a-z_]/i,
8809
8810         /**
8811          * The function used to validate alphanumeric values
8812          * @param {String} value The value
8813          */
8814         'alphanum' : function(v){
8815             return alphanum.test(v);
8816         },
8817         /**
8818          * The error text to display when the alphanumeric validation function returns false
8819          * @type String
8820          */
8821         'alphanumText' : 'This field should only contain letters, numbers and _',
8822         /**
8823          * The keystroke filter mask to be applied on alphanumeric input
8824          * @type RegExp
8825          */
8826         'alphanumMask' : /[a-z0-9_]/i
8827     };
8828 }();/*
8829  * - LGPL
8830  *
8831  * Input
8832  * 
8833  */
8834
8835 /**
8836  * @class Roo.bootstrap.Input
8837  * @extends Roo.bootstrap.Component
8838  * Bootstrap Input class
8839  * @cfg {Boolean} disabled is it disabled
8840  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8841  * @cfg {String} name name of the input
8842  * @cfg {string} fieldLabel - the label associated
8843  * @cfg {string} placeholder - placeholder to put in text.
8844  * @cfg {string}  before - input group add on before
8845  * @cfg {string} after - input group add on after
8846  * @cfg {string} size - (lg|sm) or leave empty..
8847  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8848  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8849  * @cfg {Number} md colspan out of 12 for computer-sized screens
8850  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8851  * @cfg {string} value default value of the input
8852  * @cfg {Number} labelWidth set the width of label 
8853  * @cfg {Number} labellg set the width of label (1-12)
8854  * @cfg {Number} labelmd set the width of label (1-12)
8855  * @cfg {Number} labelsm set the width of label (1-12)
8856  * @cfg {Number} labelxs set the width of label (1-12)
8857  * @cfg {String} labelAlign (top|left)
8858  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8859  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8860  * @cfg {String} indicatorpos (left|right) default left
8861  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8862  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8863
8864  * @cfg {String} align (left|center|right) Default left
8865  * @cfg {Boolean} forceFeedback (true|false) Default false
8866  * 
8867  * @constructor
8868  * Create a new Input
8869  * @param {Object} config The config object
8870  */
8871
8872 Roo.bootstrap.Input = function(config){
8873     
8874     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8875     
8876     this.addEvents({
8877         /**
8878          * @event focus
8879          * Fires when this field receives input focus.
8880          * @param {Roo.form.Field} this
8881          */
8882         focus : true,
8883         /**
8884          * @event blur
8885          * Fires when this field loses input focus.
8886          * @param {Roo.form.Field} this
8887          */
8888         blur : true,
8889         /**
8890          * @event specialkey
8891          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8892          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8893          * @param {Roo.form.Field} this
8894          * @param {Roo.EventObject} e The event object
8895          */
8896         specialkey : true,
8897         /**
8898          * @event change
8899          * Fires just before the field blurs if the field value has changed.
8900          * @param {Roo.form.Field} this
8901          * @param {Mixed} newValue The new value
8902          * @param {Mixed} oldValue The original value
8903          */
8904         change : true,
8905         /**
8906          * @event invalid
8907          * Fires after the field has been marked as invalid.
8908          * @param {Roo.form.Field} this
8909          * @param {String} msg The validation message
8910          */
8911         invalid : true,
8912         /**
8913          * @event valid
8914          * Fires after the field has been validated with no errors.
8915          * @param {Roo.form.Field} this
8916          */
8917         valid : true,
8918          /**
8919          * @event keyup
8920          * Fires after the key up
8921          * @param {Roo.form.Field} this
8922          * @param {Roo.EventObject}  e The event Object
8923          */
8924         keyup : true
8925     });
8926 };
8927
8928 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8929      /**
8930      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8931       automatic validation (defaults to "keyup").
8932      */
8933     validationEvent : "keyup",
8934      /**
8935      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8936      */
8937     validateOnBlur : true,
8938     /**
8939      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8940      */
8941     validationDelay : 250,
8942      /**
8943      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8944      */
8945     focusClass : "x-form-focus",  // not needed???
8946     
8947        
8948     /**
8949      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8950      */
8951     invalidClass : "has-warning",
8952     
8953     /**
8954      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8955      */
8956     validClass : "has-success",
8957     
8958     /**
8959      * @cfg {Boolean} hasFeedback (true|false) default true
8960      */
8961     hasFeedback : true,
8962     
8963     /**
8964      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8965      */
8966     invalidFeedbackClass : "glyphicon-warning-sign",
8967     
8968     /**
8969      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8970      */
8971     validFeedbackClass : "glyphicon-ok",
8972     
8973     /**
8974      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8975      */
8976     selectOnFocus : false,
8977     
8978      /**
8979      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8980      */
8981     maskRe : null,
8982        /**
8983      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8984      */
8985     vtype : null,
8986     
8987       /**
8988      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8989      */
8990     disableKeyFilter : false,
8991     
8992        /**
8993      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8994      */
8995     disabled : false,
8996      /**
8997      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8998      */
8999     allowBlank : true,
9000     /**
9001      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9002      */
9003     blankText : "Please complete this mandatory field",
9004     
9005      /**
9006      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9007      */
9008     minLength : 0,
9009     /**
9010      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9011      */
9012     maxLength : Number.MAX_VALUE,
9013     /**
9014      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9015      */
9016     minLengthText : "The minimum length for this field is {0}",
9017     /**
9018      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9019      */
9020     maxLengthText : "The maximum length for this field is {0}",
9021   
9022     
9023     /**
9024      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9025      * If available, this function will be called only after the basic validators all return true, and will be passed the
9026      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9027      */
9028     validator : null,
9029     /**
9030      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9031      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9032      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9033      */
9034     regex : null,
9035     /**
9036      * @cfg {String} regexText -- Depricated - use Invalid Text
9037      */
9038     regexText : "",
9039     
9040     /**
9041      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9042      */
9043     invalidText : "",
9044     
9045     
9046     
9047     autocomplete: false,
9048     
9049     
9050     fieldLabel : '',
9051     inputType : 'text',
9052     
9053     name : false,
9054     placeholder: false,
9055     before : false,
9056     after : false,
9057     size : false,
9058     hasFocus : false,
9059     preventMark: false,
9060     isFormField : true,
9061     value : '',
9062     labelWidth : 2,
9063     labelAlign : false,
9064     readOnly : false,
9065     align : false,
9066     formatedValue : false,
9067     forceFeedback : false,
9068     
9069     indicatorpos : 'left',
9070     
9071     labellg : 0,
9072     labelmd : 0,
9073     labelsm : 0,
9074     labelxs : 0,
9075     
9076     capture : '',
9077     accept : '',
9078     
9079     parentLabelAlign : function()
9080     {
9081         var parent = this;
9082         while (parent.parent()) {
9083             parent = parent.parent();
9084             if (typeof(parent.labelAlign) !='undefined') {
9085                 return parent.labelAlign;
9086             }
9087         }
9088         return 'left';
9089         
9090     },
9091     
9092     getAutoCreate : function()
9093     {
9094         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9095         
9096         var id = Roo.id();
9097         
9098         var cfg = {};
9099         
9100         if(this.inputType != 'hidden'){
9101             cfg.cls = 'form-group' //input-group
9102         }
9103         
9104         var input =  {
9105             tag: 'input',
9106             id : id,
9107             type : this.inputType,
9108             value : this.value,
9109             cls : 'form-control',
9110             placeholder : this.placeholder || '',
9111             autocomplete : this.autocomplete || 'new-password'
9112         };
9113         
9114         if(this.capture.length){
9115             input.capture = this.capture;
9116         }
9117         
9118         if(this.accept.length){
9119             input.accept = this.accept + "/*";
9120         }
9121         
9122         if(this.align){
9123             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9124         }
9125         
9126         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9127             input.maxLength = this.maxLength;
9128         }
9129         
9130         if (this.disabled) {
9131             input.disabled=true;
9132         }
9133         
9134         if (this.readOnly) {
9135             input.readonly=true;
9136         }
9137         
9138         if (this.name) {
9139             input.name = this.name;
9140         }
9141         
9142         if (this.size) {
9143             input.cls += ' input-' + this.size;
9144         }
9145         
9146         var settings=this;
9147         ['xs','sm','md','lg'].map(function(size){
9148             if (settings[size]) {
9149                 cfg.cls += ' col-' + size + '-' + settings[size];
9150             }
9151         });
9152         
9153         var inputblock = input;
9154         
9155         var feedback = {
9156             tag: 'span',
9157             cls: 'glyphicon form-control-feedback'
9158         };
9159             
9160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9161             
9162             inputblock = {
9163                 cls : 'has-feedback',
9164                 cn :  [
9165                     input,
9166                     feedback
9167                 ] 
9168             };  
9169         }
9170         
9171         if (this.before || this.after) {
9172             
9173             inputblock = {
9174                 cls : 'input-group',
9175                 cn :  [] 
9176             };
9177             
9178             if (this.before && typeof(this.before) == 'string') {
9179                 
9180                 inputblock.cn.push({
9181                     tag :'span',
9182                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9183                     html : this.before
9184                 });
9185             }
9186             if (this.before && typeof(this.before) == 'object') {
9187                 this.before = Roo.factory(this.before);
9188                 
9189                 inputblock.cn.push({
9190                     tag :'span',
9191                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9192                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9193                 });
9194             }
9195             
9196             inputblock.cn.push(input);
9197             
9198             if (this.after && typeof(this.after) == 'string') {
9199                 inputblock.cn.push({
9200                     tag :'span',
9201                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9202                     html : this.after
9203                 });
9204             }
9205             if (this.after && typeof(this.after) == 'object') {
9206                 this.after = Roo.factory(this.after);
9207                 
9208                 inputblock.cn.push({
9209                     tag :'span',
9210                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9211                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9212                 });
9213             }
9214             
9215             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9216                 inputblock.cls += ' has-feedback';
9217                 inputblock.cn.push(feedback);
9218             }
9219         };
9220         var indicator = {
9221             tag : 'i',
9222             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9223             tooltip : 'This field is required'
9224         };
9225         if (Roo.bootstrap.version == 4) {
9226             indicator = {
9227                 tag : 'i',
9228                 style : 'display-none'
9229             };
9230         }
9231         if (align ==='left' && this.fieldLabel.length) {
9232             
9233             cfg.cls += ' roo-form-group-label-left row';
9234             
9235             cfg.cn = [
9236                 indicator,
9237                 {
9238                     tag: 'label',
9239                     'for' :  id,
9240                     cls : 'control-label col-form-label',
9241                     html : this.fieldLabel
9242
9243                 },
9244                 {
9245                     cls : "", 
9246                     cn: [
9247                         inputblock
9248                     ]
9249                 }
9250             ];
9251             
9252             var labelCfg = cfg.cn[1];
9253             var contentCfg = cfg.cn[2];
9254             
9255             if(this.indicatorpos == 'right'){
9256                 cfg.cn = [
9257                     {
9258                         tag: 'label',
9259                         'for' :  id,
9260                         cls : 'control-label col-form-label',
9261                         cn : [
9262                             {
9263                                 tag : 'span',
9264                                 html : this.fieldLabel
9265                             },
9266                             indicator
9267                         ]
9268                     },
9269                     {
9270                         cls : "",
9271                         cn: [
9272                             inputblock
9273                         ]
9274                     }
9275
9276                 ];
9277                 
9278                 labelCfg = cfg.cn[0];
9279                 contentCfg = cfg.cn[1];
9280             
9281             }
9282             
9283             if(this.labelWidth > 12){
9284                 labelCfg.style = "width: " + this.labelWidth + 'px';
9285             }
9286             
9287             if(this.labelWidth < 13 && this.labelmd == 0){
9288                 this.labelmd = this.labelWidth;
9289             }
9290             
9291             if(this.labellg > 0){
9292                 labelCfg.cls += ' col-lg-' + this.labellg;
9293                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9294             }
9295             
9296             if(this.labelmd > 0){
9297                 labelCfg.cls += ' col-md-' + this.labelmd;
9298                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9299             }
9300             
9301             if(this.labelsm > 0){
9302                 labelCfg.cls += ' col-sm-' + this.labelsm;
9303                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9304             }
9305             
9306             if(this.labelxs > 0){
9307                 labelCfg.cls += ' col-xs-' + this.labelxs;
9308                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9309             }
9310             
9311             
9312         } else if ( this.fieldLabel.length) {
9313                 
9314             cfg.cn = [
9315                 {
9316                     tag : 'i',
9317                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9318                     tooltip : 'This field is required'
9319                 },
9320                 {
9321                     tag: 'label',
9322                    //cls : 'input-group-addon',
9323                     html : this.fieldLabel
9324
9325                 },
9326
9327                inputblock
9328
9329            ];
9330            
9331            if(this.indicatorpos == 'right'){
9332                 
9333                 cfg.cn = [
9334                     {
9335                         tag: 'label',
9336                        //cls : 'input-group-addon',
9337                         html : this.fieldLabel
9338
9339                     },
9340                     {
9341                         tag : 'i',
9342                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9343                         tooltip : 'This field is required'
9344                     },
9345
9346                    inputblock
9347
9348                ];
9349
9350             }
9351
9352         } else {
9353             
9354             cfg.cn = [
9355
9356                     inputblock
9357
9358             ];
9359                 
9360                 
9361         };
9362         
9363         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9364            cfg.cls += ' navbar-form';
9365         }
9366         
9367         if (this.parentType === 'NavGroup') {
9368            cfg.cls += ' navbar-form';
9369            cfg.tag = 'li';
9370         }
9371         
9372         return cfg;
9373         
9374     },
9375     /**
9376      * return the real input element.
9377      */
9378     inputEl: function ()
9379     {
9380         return this.el.select('input.form-control',true).first();
9381     },
9382     
9383     tooltipEl : function()
9384     {
9385         return this.inputEl();
9386     },
9387     
9388     indicatorEl : function()
9389     {
9390         if (Roo.bootstrap.version == 4) {
9391             return false; // not enabled in v4 yet.
9392         }
9393         
9394         var indicator = this.el.select('i.roo-required-indicator',true).first();
9395         
9396         if(!indicator){
9397             return false;
9398         }
9399         
9400         return indicator;
9401         
9402     },
9403     
9404     setDisabled : function(v)
9405     {
9406         var i  = this.inputEl().dom;
9407         if (!v) {
9408             i.removeAttribute('disabled');
9409             return;
9410             
9411         }
9412         i.setAttribute('disabled','true');
9413     },
9414     initEvents : function()
9415     {
9416           
9417         this.inputEl().on("keydown" , this.fireKey,  this);
9418         this.inputEl().on("focus", this.onFocus,  this);
9419         this.inputEl().on("blur", this.onBlur,  this);
9420         
9421         this.inputEl().relayEvent('keyup', this);
9422         
9423         this.indicator = this.indicatorEl();
9424         
9425         if(this.indicator){
9426             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9427         }
9428  
9429         // reference to original value for reset
9430         this.originalValue = this.getValue();
9431         //Roo.form.TextField.superclass.initEvents.call(this);
9432         if(this.validationEvent == 'keyup'){
9433             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9434             this.inputEl().on('keyup', this.filterValidation, this);
9435         }
9436         else if(this.validationEvent !== false){
9437             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9438         }
9439         
9440         if(this.selectOnFocus){
9441             this.on("focus", this.preFocus, this);
9442             
9443         }
9444         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9445             this.inputEl().on("keypress", this.filterKeys, this);
9446         } else {
9447             this.inputEl().relayEvent('keypress', this);
9448         }
9449        /* if(this.grow){
9450             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9451             this.el.on("click", this.autoSize,  this);
9452         }
9453         */
9454         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9455             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9456         }
9457         
9458         if (typeof(this.before) == 'object') {
9459             this.before.render(this.el.select('.roo-input-before',true).first());
9460         }
9461         if (typeof(this.after) == 'object') {
9462             this.after.render(this.el.select('.roo-input-after',true).first());
9463         }
9464         
9465         this.inputEl().on('change', this.onChange, this);
9466         
9467     },
9468     filterValidation : function(e){
9469         if(!e.isNavKeyPress()){
9470             this.validationTask.delay(this.validationDelay);
9471         }
9472     },
9473      /**
9474      * Validates the field value
9475      * @return {Boolean} True if the value is valid, else false
9476      */
9477     validate : function(){
9478         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9479         if(this.disabled || this.validateValue(this.getRawValue())){
9480             this.markValid();
9481             return true;
9482         }
9483         
9484         this.markInvalid();
9485         return false;
9486     },
9487     
9488     
9489     /**
9490      * Validates a value according to the field's validation rules and marks the field as invalid
9491      * if the validation fails
9492      * @param {Mixed} value The value to validate
9493      * @return {Boolean} True if the value is valid, else false
9494      */
9495     validateValue : function(value)
9496     {
9497         if(this.getVisibilityEl().hasClass('hidden')){
9498             return true;
9499         }
9500         
9501         if(value.length < 1)  { // if it's blank
9502             if(this.allowBlank){
9503                 return true;
9504             }
9505             return false;
9506         }
9507         
9508         if(value.length < this.minLength){
9509             return false;
9510         }
9511         if(value.length > this.maxLength){
9512             return false;
9513         }
9514         if(this.vtype){
9515             var vt = Roo.form.VTypes;
9516             if(!vt[this.vtype](value, this)){
9517                 return false;
9518             }
9519         }
9520         if(typeof this.validator == "function"){
9521             var msg = this.validator(value);
9522             if(msg !== true){
9523                 return false;
9524             }
9525             if (typeof(msg) == 'string') {
9526                 this.invalidText = msg;
9527             }
9528         }
9529         
9530         if(this.regex && !this.regex.test(value)){
9531             return false;
9532         }
9533         
9534         return true;
9535     },
9536     
9537      // private
9538     fireKey : function(e){
9539         //Roo.log('field ' + e.getKey());
9540         if(e.isNavKeyPress()){
9541             this.fireEvent("specialkey", this, e);
9542         }
9543     },
9544     focus : function (selectText){
9545         if(this.rendered){
9546             this.inputEl().focus();
9547             if(selectText === true){
9548                 this.inputEl().dom.select();
9549             }
9550         }
9551         return this;
9552     } ,
9553     
9554     onFocus : function(){
9555         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9556            // this.el.addClass(this.focusClass);
9557         }
9558         if(!this.hasFocus){
9559             this.hasFocus = true;
9560             this.startValue = this.getValue();
9561             this.fireEvent("focus", this);
9562         }
9563     },
9564     
9565     beforeBlur : Roo.emptyFn,
9566
9567     
9568     // private
9569     onBlur : function(){
9570         this.beforeBlur();
9571         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9572             //this.el.removeClass(this.focusClass);
9573         }
9574         this.hasFocus = false;
9575         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9576             this.validate();
9577         }
9578         var v = this.getValue();
9579         if(String(v) !== String(this.startValue)){
9580             this.fireEvent('change', this, v, this.startValue);
9581         }
9582         this.fireEvent("blur", this);
9583     },
9584     
9585     onChange : function(e)
9586     {
9587         var v = this.getValue();
9588         if(String(v) !== String(this.startValue)){
9589             this.fireEvent('change', this, v, this.startValue);
9590         }
9591         
9592     },
9593     
9594     /**
9595      * Resets the current field value to the originally loaded value and clears any validation messages
9596      */
9597     reset : function(){
9598         this.setValue(this.originalValue);
9599         this.validate();
9600     },
9601      /**
9602      * Returns the name of the field
9603      * @return {Mixed} name The name field
9604      */
9605     getName: function(){
9606         return this.name;
9607     },
9608      /**
9609      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9610      * @return {Mixed} value The field value
9611      */
9612     getValue : function(){
9613         
9614         var v = this.inputEl().getValue();
9615         
9616         return v;
9617     },
9618     /**
9619      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9620      * @return {Mixed} value The field value
9621      */
9622     getRawValue : function(){
9623         var v = this.inputEl().getValue();
9624         
9625         return v;
9626     },
9627     
9628     /**
9629      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9630      * @param {Mixed} value The value to set
9631      */
9632     setRawValue : function(v){
9633         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9634     },
9635     
9636     selectText : function(start, end){
9637         var v = this.getRawValue();
9638         if(v.length > 0){
9639             start = start === undefined ? 0 : start;
9640             end = end === undefined ? v.length : end;
9641             var d = this.inputEl().dom;
9642             if(d.setSelectionRange){
9643                 d.setSelectionRange(start, end);
9644             }else if(d.createTextRange){
9645                 var range = d.createTextRange();
9646                 range.moveStart("character", start);
9647                 range.moveEnd("character", v.length-end);
9648                 range.select();
9649             }
9650         }
9651     },
9652     
9653     /**
9654      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9655      * @param {Mixed} value The value to set
9656      */
9657     setValue : function(v){
9658         this.value = v;
9659         if(this.rendered){
9660             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9661             this.validate();
9662         }
9663     },
9664     
9665     /*
9666     processValue : function(value){
9667         if(this.stripCharsRe){
9668             var newValue = value.replace(this.stripCharsRe, '');
9669             if(newValue !== value){
9670                 this.setRawValue(newValue);
9671                 return newValue;
9672             }
9673         }
9674         return value;
9675     },
9676   */
9677     preFocus : function(){
9678         
9679         if(this.selectOnFocus){
9680             this.inputEl().dom.select();
9681         }
9682     },
9683     filterKeys : function(e){
9684         var k = e.getKey();
9685         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9686             return;
9687         }
9688         var c = e.getCharCode(), cc = String.fromCharCode(c);
9689         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9690             return;
9691         }
9692         if(!this.maskRe.test(cc)){
9693             e.stopEvent();
9694         }
9695     },
9696      /**
9697      * Clear any invalid styles/messages for this field
9698      */
9699     clearInvalid : function(){
9700         
9701         if(!this.el || this.preventMark){ // not rendered
9702             return;
9703         }
9704         
9705      
9706         this.el.removeClass(this.invalidClass);
9707         
9708         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709             
9710             var feedback = this.el.select('.form-control-feedback', true).first();
9711             
9712             if(feedback){
9713                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9714             }
9715             
9716         }
9717         
9718         if(this.indicator){
9719             this.indicator.removeClass('visible');
9720             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9721         }
9722         
9723         this.fireEvent('valid', this);
9724     },
9725     
9726      /**
9727      * Mark this field as valid
9728      */
9729     markValid : function()
9730     {
9731         if(!this.el  || this.preventMark){ // not rendered...
9732             return;
9733         }
9734         
9735         this.el.removeClass([this.invalidClass, this.validClass]);
9736         
9737         var feedback = this.el.select('.form-control-feedback', true).first();
9738             
9739         if(feedback){
9740             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9741         }
9742         
9743         if(this.indicator){
9744             this.indicator.removeClass('visible');
9745             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9746         }
9747         
9748         if(this.disabled){
9749             return;
9750         }
9751         
9752         if(this.allowBlank && !this.getRawValue().length){
9753             return;
9754         }
9755         
9756         this.el.addClass(this.validClass);
9757         
9758         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9759             
9760             var feedback = this.el.select('.form-control-feedback', true).first();
9761             
9762             if(feedback){
9763                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9764                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9765             }
9766             
9767         }
9768         
9769         this.fireEvent('valid', this);
9770     },
9771     
9772      /**
9773      * Mark this field as invalid
9774      * @param {String} msg The validation message
9775      */
9776     markInvalid : function(msg)
9777     {
9778         if(!this.el  || this.preventMark){ // not rendered
9779             return;
9780         }
9781         
9782         this.el.removeClass([this.invalidClass, this.validClass]);
9783         
9784         var feedback = this.el.select('.form-control-feedback', true).first();
9785             
9786         if(feedback){
9787             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9788         }
9789
9790         if(this.disabled){
9791             return;
9792         }
9793         
9794         if(this.allowBlank && !this.getRawValue().length){
9795             return;
9796         }
9797         
9798         if(this.indicator){
9799             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9800             this.indicator.addClass('visible');
9801         }
9802         
9803         this.el.addClass(this.invalidClass);
9804         
9805         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9806             
9807             var feedback = this.el.select('.form-control-feedback', true).first();
9808             
9809             if(feedback){
9810                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9811                 
9812                 if(this.getValue().length || this.forceFeedback){
9813                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9814                 }
9815                 
9816             }
9817             
9818         }
9819         
9820         this.fireEvent('invalid', this, msg);
9821     },
9822     // private
9823     SafariOnKeyDown : function(event)
9824     {
9825         // this is a workaround for a password hang bug on chrome/ webkit.
9826         if (this.inputEl().dom.type != 'password') {
9827             return;
9828         }
9829         
9830         var isSelectAll = false;
9831         
9832         if(this.inputEl().dom.selectionEnd > 0){
9833             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9834         }
9835         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9836             event.preventDefault();
9837             this.setValue('');
9838             return;
9839         }
9840         
9841         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9842             
9843             event.preventDefault();
9844             // this is very hacky as keydown always get's upper case.
9845             //
9846             var cc = String.fromCharCode(event.getCharCode());
9847             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9848             
9849         }
9850     },
9851     adjustWidth : function(tag, w){
9852         tag = tag.toLowerCase();
9853         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9854             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9855                 if(tag == 'input'){
9856                     return w + 2;
9857                 }
9858                 if(tag == 'textarea'){
9859                     return w-2;
9860                 }
9861             }else if(Roo.isOpera){
9862                 if(tag == 'input'){
9863                     return w + 2;
9864                 }
9865                 if(tag == 'textarea'){
9866                     return w-2;
9867                 }
9868             }
9869         }
9870         return w;
9871     },
9872     
9873     setFieldLabel : function(v)
9874     {
9875         if(!this.rendered){
9876             return;
9877         }
9878         
9879         if(this.indicatorEl()){
9880             var ar = this.el.select('label > span',true);
9881             
9882             if (ar.elements.length) {
9883                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9884                 this.fieldLabel = v;
9885                 return;
9886             }
9887             
9888             var br = this.el.select('label',true);
9889             
9890             if(br.elements.length) {
9891                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9892                 this.fieldLabel = v;
9893                 return;
9894             }
9895             
9896             Roo.log('Cannot Found any of label > span || label in input');
9897             return;
9898         }
9899         
9900         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9901         this.fieldLabel = v;
9902         
9903         
9904     }
9905 });
9906
9907  
9908 /*
9909  * - LGPL
9910  *
9911  * Input
9912  * 
9913  */
9914
9915 /**
9916  * @class Roo.bootstrap.TextArea
9917  * @extends Roo.bootstrap.Input
9918  * Bootstrap TextArea class
9919  * @cfg {Number} cols Specifies the visible width of a text area
9920  * @cfg {Number} rows Specifies the visible number of lines in a text area
9921  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9922  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9923  * @cfg {string} html text
9924  * 
9925  * @constructor
9926  * Create a new TextArea
9927  * @param {Object} config The config object
9928  */
9929
9930 Roo.bootstrap.TextArea = function(config){
9931     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9932    
9933 };
9934
9935 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9936      
9937     cols : false,
9938     rows : 5,
9939     readOnly : false,
9940     warp : 'soft',
9941     resize : false,
9942     value: false,
9943     html: false,
9944     
9945     getAutoCreate : function(){
9946         
9947         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9948         
9949         var id = Roo.id();
9950         
9951         var cfg = {};
9952         
9953         if(this.inputType != 'hidden'){
9954             cfg.cls = 'form-group' //input-group
9955         }
9956         
9957         var input =  {
9958             tag: 'textarea',
9959             id : id,
9960             warp : this.warp,
9961             rows : this.rows,
9962             value : this.value || '',
9963             html: this.html || '',
9964             cls : 'form-control',
9965             placeholder : this.placeholder || '' 
9966             
9967         };
9968         
9969         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9970             input.maxLength = this.maxLength;
9971         }
9972         
9973         if(this.resize){
9974             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9975         }
9976         
9977         if(this.cols){
9978             input.cols = this.cols;
9979         }
9980         
9981         if (this.readOnly) {
9982             input.readonly = true;
9983         }
9984         
9985         if (this.name) {
9986             input.name = this.name;
9987         }
9988         
9989         if (this.size) {
9990             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9991         }
9992         
9993         var settings=this;
9994         ['xs','sm','md','lg'].map(function(size){
9995             if (settings[size]) {
9996                 cfg.cls += ' col-' + size + '-' + settings[size];
9997             }
9998         });
9999         
10000         var inputblock = input;
10001         
10002         if(this.hasFeedback && !this.allowBlank){
10003             
10004             var feedback = {
10005                 tag: 'span',
10006                 cls: 'glyphicon form-control-feedback'
10007             };
10008
10009             inputblock = {
10010                 cls : 'has-feedback',
10011                 cn :  [
10012                     input,
10013                     feedback
10014                 ] 
10015             };  
10016         }
10017         
10018         
10019         if (this.before || this.after) {
10020             
10021             inputblock = {
10022                 cls : 'input-group',
10023                 cn :  [] 
10024             };
10025             if (this.before) {
10026                 inputblock.cn.push({
10027                     tag :'span',
10028                     cls : 'input-group-addon',
10029                     html : this.before
10030                 });
10031             }
10032             
10033             inputblock.cn.push(input);
10034             
10035             if(this.hasFeedback && !this.allowBlank){
10036                 inputblock.cls += ' has-feedback';
10037                 inputblock.cn.push(feedback);
10038             }
10039             
10040             if (this.after) {
10041                 inputblock.cn.push({
10042                     tag :'span',
10043                     cls : 'input-group-addon',
10044                     html : this.after
10045                 });
10046             }
10047             
10048         }
10049         
10050         if (align ==='left' && this.fieldLabel.length) {
10051             cfg.cn = [
10052                 {
10053                     tag: 'label',
10054                     'for' :  id,
10055                     cls : 'control-label',
10056                     html : this.fieldLabel
10057                 },
10058                 {
10059                     cls : "",
10060                     cn: [
10061                         inputblock
10062                     ]
10063                 }
10064
10065             ];
10066             
10067             if(this.labelWidth > 12){
10068                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10069             }
10070
10071             if(this.labelWidth < 13 && this.labelmd == 0){
10072                 this.labelmd = this.labelWidth;
10073             }
10074
10075             if(this.labellg > 0){
10076                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10077                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10078             }
10079
10080             if(this.labelmd > 0){
10081                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10082                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10083             }
10084
10085             if(this.labelsm > 0){
10086                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10087                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10088             }
10089
10090             if(this.labelxs > 0){
10091                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10092                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10093             }
10094             
10095         } else if ( this.fieldLabel.length) {
10096             cfg.cn = [
10097
10098                {
10099                    tag: 'label',
10100                    //cls : 'input-group-addon',
10101                    html : this.fieldLabel
10102
10103                },
10104
10105                inputblock
10106
10107            ];
10108
10109         } else {
10110
10111             cfg.cn = [
10112
10113                 inputblock
10114
10115             ];
10116                 
10117         }
10118         
10119         if (this.disabled) {
10120             input.disabled=true;
10121         }
10122         
10123         return cfg;
10124         
10125     },
10126     /**
10127      * return the real textarea element.
10128      */
10129     inputEl: function ()
10130     {
10131         return this.el.select('textarea.form-control',true).first();
10132     },
10133     
10134     /**
10135      * Clear any invalid styles/messages for this field
10136      */
10137     clearInvalid : function()
10138     {
10139         
10140         if(!this.el || this.preventMark){ // not rendered
10141             return;
10142         }
10143         
10144         var label = this.el.select('label', true).first();
10145         var icon = this.el.select('i.fa-star', true).first();
10146         
10147         if(label && icon){
10148             icon.remove();
10149         }
10150         
10151         this.el.removeClass(this.invalidClass);
10152         
10153         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10154             
10155             var feedback = this.el.select('.form-control-feedback', true).first();
10156             
10157             if(feedback){
10158                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10159             }
10160             
10161         }
10162         
10163         this.fireEvent('valid', this);
10164     },
10165     
10166      /**
10167      * Mark this field as valid
10168      */
10169     markValid : function()
10170     {
10171         if(!this.el  || this.preventMark){ // not rendered
10172             return;
10173         }
10174         
10175         this.el.removeClass([this.invalidClass, this.validClass]);
10176         
10177         var feedback = this.el.select('.form-control-feedback', true).first();
10178             
10179         if(feedback){
10180             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10181         }
10182
10183         if(this.disabled || this.allowBlank){
10184             return;
10185         }
10186         
10187         var label = this.el.select('label', true).first();
10188         var icon = this.el.select('i.fa-star', true).first();
10189         
10190         if(label && icon){
10191             icon.remove();
10192         }
10193         
10194         this.el.addClass(this.validClass);
10195         
10196         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10197             
10198             var feedback = this.el.select('.form-control-feedback', true).first();
10199             
10200             if(feedback){
10201                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10202                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10203             }
10204             
10205         }
10206         
10207         this.fireEvent('valid', this);
10208     },
10209     
10210      /**
10211      * Mark this field as invalid
10212      * @param {String} msg The validation message
10213      */
10214     markInvalid : function(msg)
10215     {
10216         if(!this.el  || this.preventMark){ // not rendered
10217             return;
10218         }
10219         
10220         this.el.removeClass([this.invalidClass, this.validClass]);
10221         
10222         var feedback = this.el.select('.form-control-feedback', true).first();
10223             
10224         if(feedback){
10225             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10226         }
10227
10228         if(this.disabled || this.allowBlank){
10229             return;
10230         }
10231         
10232         var label = this.el.select('label', true).first();
10233         var icon = this.el.select('i.fa-star', true).first();
10234         
10235         if(!this.getValue().length && label && !icon){
10236             this.el.createChild({
10237                 tag : 'i',
10238                 cls : 'text-danger fa fa-lg fa-star',
10239                 tooltip : 'This field is required',
10240                 style : 'margin-right:5px;'
10241             }, label, true);
10242         }
10243
10244         this.el.addClass(this.invalidClass);
10245         
10246         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10247             
10248             var feedback = this.el.select('.form-control-feedback', true).first();
10249             
10250             if(feedback){
10251                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10252                 
10253                 if(this.getValue().length || this.forceFeedback){
10254                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10255                 }
10256                 
10257             }
10258             
10259         }
10260         
10261         this.fireEvent('invalid', this, msg);
10262     }
10263 });
10264
10265  
10266 /*
10267  * - LGPL
10268  *
10269  * trigger field - base class for combo..
10270  * 
10271  */
10272  
10273 /**
10274  * @class Roo.bootstrap.TriggerField
10275  * @extends Roo.bootstrap.Input
10276  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10277  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10278  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10279  * for which you can provide a custom implementation.  For example:
10280  * <pre><code>
10281 var trigger = new Roo.bootstrap.TriggerField();
10282 trigger.onTriggerClick = myTriggerFn;
10283 trigger.applyTo('my-field');
10284 </code></pre>
10285  *
10286  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10287  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10288  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10289  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10290  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10291
10292  * @constructor
10293  * Create a new TriggerField.
10294  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10295  * to the base TextField)
10296  */
10297 Roo.bootstrap.TriggerField = function(config){
10298     this.mimicing = false;
10299     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10300 };
10301
10302 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10303     /**
10304      * @cfg {String} triggerClass A CSS class to apply to the trigger
10305      */
10306      /**
10307      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10308      */
10309     hideTrigger:false,
10310
10311     /**
10312      * @cfg {Boolean} removable (true|false) special filter default false
10313      */
10314     removable : false,
10315     
10316     /** @cfg {Boolean} grow @hide */
10317     /** @cfg {Number} growMin @hide */
10318     /** @cfg {Number} growMax @hide */
10319
10320     /**
10321      * @hide 
10322      * @method
10323      */
10324     autoSize: Roo.emptyFn,
10325     // private
10326     monitorTab : true,
10327     // private
10328     deferHeight : true,
10329
10330     
10331     actionMode : 'wrap',
10332     
10333     caret : false,
10334     
10335     
10336     getAutoCreate : function(){
10337        
10338         var align = this.labelAlign || this.parentLabelAlign();
10339         
10340         var id = Roo.id();
10341         
10342         var cfg = {
10343             cls: 'form-group' //input-group
10344         };
10345         
10346         
10347         var input =  {
10348             tag: 'input',
10349             id : id,
10350             type : this.inputType,
10351             cls : 'form-control',
10352             autocomplete: 'new-password',
10353             placeholder : this.placeholder || '' 
10354             
10355         };
10356         if (this.name) {
10357             input.name = this.name;
10358         }
10359         if (this.size) {
10360             input.cls += ' input-' + this.size;
10361         }
10362         
10363         if (this.disabled) {
10364             input.disabled=true;
10365         }
10366         
10367         var inputblock = input;
10368         
10369         if(this.hasFeedback && !this.allowBlank){
10370             
10371             var feedback = {
10372                 tag: 'span',
10373                 cls: 'glyphicon form-control-feedback'
10374             };
10375             
10376             if(this.removable && !this.editable && !this.tickable){
10377                 inputblock = {
10378                     cls : 'has-feedback',
10379                     cn :  [
10380                         inputblock,
10381                         {
10382                             tag: 'button',
10383                             html : 'x',
10384                             cls : 'roo-combo-removable-btn close'
10385                         },
10386                         feedback
10387                     ] 
10388                 };
10389             } else {
10390                 inputblock = {
10391                     cls : 'has-feedback',
10392                     cn :  [
10393                         inputblock,
10394                         feedback
10395                     ] 
10396                 };
10397             }
10398
10399         } else {
10400             if(this.removable && !this.editable && !this.tickable){
10401                 inputblock = {
10402                     cls : 'roo-removable',
10403                     cn :  [
10404                         inputblock,
10405                         {
10406                             tag: 'button',
10407                             html : 'x',
10408                             cls : 'roo-combo-removable-btn close'
10409                         }
10410                     ] 
10411                 };
10412             }
10413         }
10414         
10415         if (this.before || this.after) {
10416             
10417             inputblock = {
10418                 cls : 'input-group',
10419                 cn :  [] 
10420             };
10421             if (this.before) {
10422                 inputblock.cn.push({
10423                     tag :'span',
10424                     cls : 'input-group-addon input-group-prepend input-group-text',
10425                     html : this.before
10426                 });
10427             }
10428             
10429             inputblock.cn.push(input);
10430             
10431             if(this.hasFeedback && !this.allowBlank){
10432                 inputblock.cls += ' has-feedback';
10433                 inputblock.cn.push(feedback);
10434             }
10435             
10436             if (this.after) {
10437                 inputblock.cn.push({
10438                     tag :'span',
10439                     cls : 'input-group-addon input-group-append input-group-text',
10440                     html : this.after
10441                 });
10442             }
10443             
10444         };
10445         
10446       
10447         
10448         var ibwrap = inputblock;
10449         
10450         if(this.multiple){
10451             ibwrap = {
10452                 tag: 'ul',
10453                 cls: 'roo-select2-choices',
10454                 cn:[
10455                     {
10456                         tag: 'li',
10457                         cls: 'roo-select2-search-field',
10458                         cn: [
10459
10460                             inputblock
10461                         ]
10462                     }
10463                 ]
10464             };
10465                 
10466         }
10467         
10468         var combobox = {
10469             cls: 'roo-select2-container input-group',
10470             cn: [
10471                  {
10472                     tag: 'input',
10473                     type : 'hidden',
10474                     cls: 'form-hidden-field'
10475                 },
10476                 ibwrap
10477             ]
10478         };
10479         
10480         if(!this.multiple && this.showToggleBtn){
10481             
10482             var caret = {
10483                         tag: 'span',
10484                         cls: 'caret'
10485              };
10486             if (this.caret != false) {
10487                 caret = {
10488                      tag: 'i',
10489                      cls: 'fa fa-' + this.caret
10490                 };
10491                 
10492             }
10493             
10494             combobox.cn.push({
10495                 tag :'span',
10496                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10497                 cn : [
10498                     caret,
10499                     {
10500                         tag: 'span',
10501                         cls: 'combobox-clear',
10502                         cn  : [
10503                             {
10504                                 tag : 'i',
10505                                 cls: 'icon-remove'
10506                             }
10507                         ]
10508                     }
10509                 ]
10510
10511             })
10512         }
10513         
10514         if(this.multiple){
10515             combobox.cls += ' roo-select2-container-multi';
10516         }
10517          var indicator = {
10518             tag : 'i',
10519             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10520             tooltip : 'This field is required'
10521         };
10522         if (Roo.bootstrap.version == 4) {
10523             indicator = {
10524                 tag : 'i',
10525                 style : 'display:none'
10526             };
10527         }
10528         
10529         
10530         if (align ==='left' && this.fieldLabel.length) {
10531             
10532             cfg.cls += ' roo-form-group-label-left row';
10533
10534             cfg.cn = [
10535                 indicator,
10536                 {
10537                     tag: 'label',
10538                     'for' :  id,
10539                     cls : 'control-label',
10540                     html : this.fieldLabel
10541
10542                 },
10543                 {
10544                     cls : "", 
10545                     cn: [
10546                         combobox
10547                     ]
10548                 }
10549
10550             ];
10551             
10552             var labelCfg = cfg.cn[1];
10553             var contentCfg = cfg.cn[2];
10554             
10555             if(this.indicatorpos == 'right'){
10556                 cfg.cn = [
10557                     {
10558                         tag: 'label',
10559                         'for' :  id,
10560                         cls : 'control-label',
10561                         cn : [
10562                             {
10563                                 tag : 'span',
10564                                 html : this.fieldLabel
10565                             },
10566                             indicator
10567                         ]
10568                     },
10569                     {
10570                         cls : "", 
10571                         cn: [
10572                             combobox
10573                         ]
10574                     }
10575
10576                 ];
10577                 
10578                 labelCfg = cfg.cn[0];
10579                 contentCfg = cfg.cn[1];
10580             }
10581             
10582             if(this.labelWidth > 12){
10583                 labelCfg.style = "width: " + this.labelWidth + 'px';
10584             }
10585             
10586             if(this.labelWidth < 13 && this.labelmd == 0){
10587                 this.labelmd = this.labelWidth;
10588             }
10589             
10590             if(this.labellg > 0){
10591                 labelCfg.cls += ' col-lg-' + this.labellg;
10592                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10593             }
10594             
10595             if(this.labelmd > 0){
10596                 labelCfg.cls += ' col-md-' + this.labelmd;
10597                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10598             }
10599             
10600             if(this.labelsm > 0){
10601                 labelCfg.cls += ' col-sm-' + this.labelsm;
10602                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10603             }
10604             
10605             if(this.labelxs > 0){
10606                 labelCfg.cls += ' col-xs-' + this.labelxs;
10607                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10608             }
10609             
10610         } else if ( this.fieldLabel.length) {
10611 //                Roo.log(" label");
10612             cfg.cn = [
10613                 indicator,
10614                {
10615                    tag: 'label',
10616                    //cls : 'input-group-addon',
10617                    html : this.fieldLabel
10618
10619                },
10620
10621                combobox
10622
10623             ];
10624             
10625             if(this.indicatorpos == 'right'){
10626                 
10627                 cfg.cn = [
10628                     {
10629                        tag: 'label',
10630                        cn : [
10631                            {
10632                                tag : 'span',
10633                                html : this.fieldLabel
10634                            },
10635                            indicator
10636                        ]
10637
10638                     },
10639                     combobox
10640
10641                 ];
10642
10643             }
10644
10645         } else {
10646             
10647 //                Roo.log(" no label && no align");
10648                 cfg = combobox
10649                      
10650                 
10651         }
10652         
10653         var settings=this;
10654         ['xs','sm','md','lg'].map(function(size){
10655             if (settings[size]) {
10656                 cfg.cls += ' col-' + size + '-' + settings[size];
10657             }
10658         });
10659         
10660         return cfg;
10661         
10662     },
10663     
10664     
10665     
10666     // private
10667     onResize : function(w, h){
10668 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10669 //        if(typeof w == 'number'){
10670 //            var x = w - this.trigger.getWidth();
10671 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10672 //            this.trigger.setStyle('left', x+'px');
10673 //        }
10674     },
10675
10676     // private
10677     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10678
10679     // private
10680     getResizeEl : function(){
10681         return this.inputEl();
10682     },
10683
10684     // private
10685     getPositionEl : function(){
10686         return this.inputEl();
10687     },
10688
10689     // private
10690     alignErrorIcon : function(){
10691         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10692     },
10693
10694     // private
10695     initEvents : function(){
10696         
10697         this.createList();
10698         
10699         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10700         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10701         if(!this.multiple && this.showToggleBtn){
10702             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10703             if(this.hideTrigger){
10704                 this.trigger.setDisplayed(false);
10705             }
10706             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10707         }
10708         
10709         if(this.multiple){
10710             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10711         }
10712         
10713         if(this.removable && !this.editable && !this.tickable){
10714             var close = this.closeTriggerEl();
10715             
10716             if(close){
10717                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10718                 close.on('click', this.removeBtnClick, this, close);
10719             }
10720         }
10721         
10722         //this.trigger.addClassOnOver('x-form-trigger-over');
10723         //this.trigger.addClassOnClick('x-form-trigger-click');
10724         
10725         //if(!this.width){
10726         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10727         //}
10728     },
10729     
10730     closeTriggerEl : function()
10731     {
10732         var close = this.el.select('.roo-combo-removable-btn', true).first();
10733         return close ? close : false;
10734     },
10735     
10736     removeBtnClick : function(e, h, el)
10737     {
10738         e.preventDefault();
10739         
10740         if(this.fireEvent("remove", this) !== false){
10741             this.reset();
10742             this.fireEvent("afterremove", this)
10743         }
10744     },
10745     
10746     createList : function()
10747     {
10748         this.list = Roo.get(document.body).createChild({
10749             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10750             cls: 'typeahead typeahead-long dropdown-menu',
10751             style: 'display:none'
10752         });
10753         
10754         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10755         
10756     },
10757
10758     // private
10759     initTrigger : function(){
10760        
10761     },
10762
10763     // private
10764     onDestroy : function(){
10765         if(this.trigger){
10766             this.trigger.removeAllListeners();
10767           //  this.trigger.remove();
10768         }
10769         //if(this.wrap){
10770         //    this.wrap.remove();
10771         //}
10772         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10773     },
10774
10775     // private
10776     onFocus : function(){
10777         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10778         /*
10779         if(!this.mimicing){
10780             this.wrap.addClass('x-trigger-wrap-focus');
10781             this.mimicing = true;
10782             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10783             if(this.monitorTab){
10784                 this.el.on("keydown", this.checkTab, this);
10785             }
10786         }
10787         */
10788     },
10789
10790     // private
10791     checkTab : function(e){
10792         if(e.getKey() == e.TAB){
10793             this.triggerBlur();
10794         }
10795     },
10796
10797     // private
10798     onBlur : function(){
10799         // do nothing
10800     },
10801
10802     // private
10803     mimicBlur : function(e, t){
10804         /*
10805         if(!this.wrap.contains(t) && this.validateBlur()){
10806             this.triggerBlur();
10807         }
10808         */
10809     },
10810
10811     // private
10812     triggerBlur : function(){
10813         this.mimicing = false;
10814         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10815         if(this.monitorTab){
10816             this.el.un("keydown", this.checkTab, this);
10817         }
10818         //this.wrap.removeClass('x-trigger-wrap-focus');
10819         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10820     },
10821
10822     // private
10823     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10824     validateBlur : function(e, t){
10825         return true;
10826     },
10827
10828     // private
10829     onDisable : function(){
10830         this.inputEl().dom.disabled = true;
10831         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10832         //if(this.wrap){
10833         //    this.wrap.addClass('x-item-disabled');
10834         //}
10835     },
10836
10837     // private
10838     onEnable : function(){
10839         this.inputEl().dom.disabled = false;
10840         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10841         //if(this.wrap){
10842         //    this.el.removeClass('x-item-disabled');
10843         //}
10844     },
10845
10846     // private
10847     onShow : function(){
10848         var ae = this.getActionEl();
10849         
10850         if(ae){
10851             ae.dom.style.display = '';
10852             ae.dom.style.visibility = 'visible';
10853         }
10854     },
10855
10856     // private
10857     
10858     onHide : function(){
10859         var ae = this.getActionEl();
10860         ae.dom.style.display = 'none';
10861     },
10862
10863     /**
10864      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10865      * by an implementing function.
10866      * @method
10867      * @param {EventObject} e
10868      */
10869     onTriggerClick : Roo.emptyFn
10870 });
10871  /*
10872  * Based on:
10873  * Ext JS Library 1.1.1
10874  * Copyright(c) 2006-2007, Ext JS, LLC.
10875  *
10876  * Originally Released Under LGPL - original licence link has changed is not relivant.
10877  *
10878  * Fork - LGPL
10879  * <script type="text/javascript">
10880  */
10881
10882
10883 /**
10884  * @class Roo.data.SortTypes
10885  * @singleton
10886  * Defines the default sorting (casting?) comparison functions used when sorting data.
10887  */
10888 Roo.data.SortTypes = {
10889     /**
10890      * Default sort that does nothing
10891      * @param {Mixed} s The value being converted
10892      * @return {Mixed} The comparison value
10893      */
10894     none : function(s){
10895         return s;
10896     },
10897     
10898     /**
10899      * The regular expression used to strip tags
10900      * @type {RegExp}
10901      * @property
10902      */
10903     stripTagsRE : /<\/?[^>]+>/gi,
10904     
10905     /**
10906      * Strips all HTML tags to sort on text only
10907      * @param {Mixed} s The value being converted
10908      * @return {String} The comparison value
10909      */
10910     asText : function(s){
10911         return String(s).replace(this.stripTagsRE, "");
10912     },
10913     
10914     /**
10915      * Strips all HTML tags to sort on text only - Case insensitive
10916      * @param {Mixed} s The value being converted
10917      * @return {String} The comparison value
10918      */
10919     asUCText : function(s){
10920         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10921     },
10922     
10923     /**
10924      * Case insensitive string
10925      * @param {Mixed} s The value being converted
10926      * @return {String} The comparison value
10927      */
10928     asUCString : function(s) {
10929         return String(s).toUpperCase();
10930     },
10931     
10932     /**
10933      * Date sorting
10934      * @param {Mixed} s The value being converted
10935      * @return {Number} The comparison value
10936      */
10937     asDate : function(s) {
10938         if(!s){
10939             return 0;
10940         }
10941         if(s instanceof Date){
10942             return s.getTime();
10943         }
10944         return Date.parse(String(s));
10945     },
10946     
10947     /**
10948      * Float sorting
10949      * @param {Mixed} s The value being converted
10950      * @return {Float} The comparison value
10951      */
10952     asFloat : function(s) {
10953         var val = parseFloat(String(s).replace(/,/g, ""));
10954         if(isNaN(val)) {
10955             val = 0;
10956         }
10957         return val;
10958     },
10959     
10960     /**
10961      * Integer sorting
10962      * @param {Mixed} s The value being converted
10963      * @return {Number} The comparison value
10964      */
10965     asInt : function(s) {
10966         var val = parseInt(String(s).replace(/,/g, ""));
10967         if(isNaN(val)) {
10968             val = 0;
10969         }
10970         return val;
10971     }
10972 };/*
10973  * Based on:
10974  * Ext JS Library 1.1.1
10975  * Copyright(c) 2006-2007, Ext JS, LLC.
10976  *
10977  * Originally Released Under LGPL - original licence link has changed is not relivant.
10978  *
10979  * Fork - LGPL
10980  * <script type="text/javascript">
10981  */
10982
10983 /**
10984 * @class Roo.data.Record
10985  * Instances of this class encapsulate both record <em>definition</em> information, and record
10986  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10987  * to access Records cached in an {@link Roo.data.Store} object.<br>
10988  * <p>
10989  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10990  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10991  * objects.<br>
10992  * <p>
10993  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10994  * @constructor
10995  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10996  * {@link #create}. The parameters are the same.
10997  * @param {Array} data An associative Array of data values keyed by the field name.
10998  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10999  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11000  * not specified an integer id is generated.
11001  */
11002 Roo.data.Record = function(data, id){
11003     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11004     this.data = data;
11005 };
11006
11007 /**
11008  * Generate a constructor for a specific record layout.
11009  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11010  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11011  * Each field definition object may contain the following properties: <ul>
11012  * <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,
11013  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11014  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11015  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11016  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11017  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11018  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11019  * this may be omitted.</p></li>
11020  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11021  * <ul><li>auto (Default, implies no conversion)</li>
11022  * <li>string</li>
11023  * <li>int</li>
11024  * <li>float</li>
11025  * <li>boolean</li>
11026  * <li>date</li></ul></p></li>
11027  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11028  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11029  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11030  * by the Reader into an object that will be stored in the Record. It is passed the
11031  * following parameters:<ul>
11032  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11033  * </ul></p></li>
11034  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11035  * </ul>
11036  * <br>usage:<br><pre><code>
11037 var TopicRecord = Roo.data.Record.create(
11038     {name: 'title', mapping: 'topic_title'},
11039     {name: 'author', mapping: 'username'},
11040     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11041     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11042     {name: 'lastPoster', mapping: 'user2'},
11043     {name: 'excerpt', mapping: 'post_text'}
11044 );
11045
11046 var myNewRecord = new TopicRecord({
11047     title: 'Do my job please',
11048     author: 'noobie',
11049     totalPosts: 1,
11050     lastPost: new Date(),
11051     lastPoster: 'Animal',
11052     excerpt: 'No way dude!'
11053 });
11054 myStore.add(myNewRecord);
11055 </code></pre>
11056  * @method create
11057  * @static
11058  */
11059 Roo.data.Record.create = function(o){
11060     var f = function(){
11061         f.superclass.constructor.apply(this, arguments);
11062     };
11063     Roo.extend(f, Roo.data.Record);
11064     var p = f.prototype;
11065     p.fields = new Roo.util.MixedCollection(false, function(field){
11066         return field.name;
11067     });
11068     for(var i = 0, len = o.length; i < len; i++){
11069         p.fields.add(new Roo.data.Field(o[i]));
11070     }
11071     f.getField = function(name){
11072         return p.fields.get(name);  
11073     };
11074     return f;
11075 };
11076
11077 Roo.data.Record.AUTO_ID = 1000;
11078 Roo.data.Record.EDIT = 'edit';
11079 Roo.data.Record.REJECT = 'reject';
11080 Roo.data.Record.COMMIT = 'commit';
11081
11082 Roo.data.Record.prototype = {
11083     /**
11084      * Readonly flag - true if this record has been modified.
11085      * @type Boolean
11086      */
11087     dirty : false,
11088     editing : false,
11089     error: null,
11090     modified: null,
11091
11092     // private
11093     join : function(store){
11094         this.store = store;
11095     },
11096
11097     /**
11098      * Set the named field to the specified value.
11099      * @param {String} name The name of the field to set.
11100      * @param {Object} value The value to set the field to.
11101      */
11102     set : function(name, value){
11103         if(this.data[name] == value){
11104             return;
11105         }
11106         this.dirty = true;
11107         if(!this.modified){
11108             this.modified = {};
11109         }
11110         if(typeof this.modified[name] == 'undefined'){
11111             this.modified[name] = this.data[name];
11112         }
11113         this.data[name] = value;
11114         if(!this.editing && this.store){
11115             this.store.afterEdit(this);
11116         }       
11117     },
11118
11119     /**
11120      * Get the value of the named field.
11121      * @param {String} name The name of the field to get the value of.
11122      * @return {Object} The value of the field.
11123      */
11124     get : function(name){
11125         return this.data[name]; 
11126     },
11127
11128     // private
11129     beginEdit : function(){
11130         this.editing = true;
11131         this.modified = {}; 
11132     },
11133
11134     // private
11135     cancelEdit : function(){
11136         this.editing = false;
11137         delete this.modified;
11138     },
11139
11140     // private
11141     endEdit : function(){
11142         this.editing = false;
11143         if(this.dirty && this.store){
11144             this.store.afterEdit(this);
11145         }
11146     },
11147
11148     /**
11149      * Usually called by the {@link Roo.data.Store} which owns the Record.
11150      * Rejects all changes made to the Record since either creation, or the last commit operation.
11151      * Modified fields are reverted to their original values.
11152      * <p>
11153      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11154      * of reject operations.
11155      */
11156     reject : function(){
11157         var m = this.modified;
11158         for(var n in m){
11159             if(typeof m[n] != "function"){
11160                 this.data[n] = m[n];
11161             }
11162         }
11163         this.dirty = false;
11164         delete this.modified;
11165         this.editing = false;
11166         if(this.store){
11167             this.store.afterReject(this);
11168         }
11169     },
11170
11171     /**
11172      * Usually called by the {@link Roo.data.Store} which owns the Record.
11173      * Commits all changes made to the Record since either creation, or the last commit operation.
11174      * <p>
11175      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11176      * of commit operations.
11177      */
11178     commit : function(){
11179         this.dirty = false;
11180         delete this.modified;
11181         this.editing = false;
11182         if(this.store){
11183             this.store.afterCommit(this);
11184         }
11185     },
11186
11187     // private
11188     hasError : function(){
11189         return this.error != null;
11190     },
11191
11192     // private
11193     clearError : function(){
11194         this.error = null;
11195     },
11196
11197     /**
11198      * Creates a copy of this record.
11199      * @param {String} id (optional) A new record id if you don't want to use this record's id
11200      * @return {Record}
11201      */
11202     copy : function(newId) {
11203         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11204     }
11205 };/*
11206  * Based on:
11207  * Ext JS Library 1.1.1
11208  * Copyright(c) 2006-2007, Ext JS, LLC.
11209  *
11210  * Originally Released Under LGPL - original licence link has changed is not relivant.
11211  *
11212  * Fork - LGPL
11213  * <script type="text/javascript">
11214  */
11215
11216
11217
11218 /**
11219  * @class Roo.data.Store
11220  * @extends Roo.util.Observable
11221  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11222  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11223  * <p>
11224  * 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
11225  * has no knowledge of the format of the data returned by the Proxy.<br>
11226  * <p>
11227  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11228  * instances from the data object. These records are cached and made available through accessor functions.
11229  * @constructor
11230  * Creates a new Store.
11231  * @param {Object} config A config object containing the objects needed for the Store to access data,
11232  * and read the data into Records.
11233  */
11234 Roo.data.Store = function(config){
11235     this.data = new Roo.util.MixedCollection(false);
11236     this.data.getKey = function(o){
11237         return o.id;
11238     };
11239     this.baseParams = {};
11240     // private
11241     this.paramNames = {
11242         "start" : "start",
11243         "limit" : "limit",
11244         "sort" : "sort",
11245         "dir" : "dir",
11246         "multisort" : "_multisort"
11247     };
11248
11249     if(config && config.data){
11250         this.inlineData = config.data;
11251         delete config.data;
11252     }
11253
11254     Roo.apply(this, config);
11255     
11256     if(this.reader){ // reader passed
11257         this.reader = Roo.factory(this.reader, Roo.data);
11258         this.reader.xmodule = this.xmodule || false;
11259         if(!this.recordType){
11260             this.recordType = this.reader.recordType;
11261         }
11262         if(this.reader.onMetaChange){
11263             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11264         }
11265     }
11266
11267     if(this.recordType){
11268         this.fields = this.recordType.prototype.fields;
11269     }
11270     this.modified = [];
11271
11272     this.addEvents({
11273         /**
11274          * @event datachanged
11275          * Fires when the data cache has changed, and a widget which is using this Store
11276          * as a Record cache should refresh its view.
11277          * @param {Store} this
11278          */
11279         datachanged : true,
11280         /**
11281          * @event metachange
11282          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11283          * @param {Store} this
11284          * @param {Object} meta The JSON metadata
11285          */
11286         metachange : true,
11287         /**
11288          * @event add
11289          * Fires when Records have been added to the Store
11290          * @param {Store} this
11291          * @param {Roo.data.Record[]} records The array of Records added
11292          * @param {Number} index The index at which the record(s) were added
11293          */
11294         add : true,
11295         /**
11296          * @event remove
11297          * Fires when a Record has been removed from the Store
11298          * @param {Store} this
11299          * @param {Roo.data.Record} record The Record that was removed
11300          * @param {Number} index The index at which the record was removed
11301          */
11302         remove : true,
11303         /**
11304          * @event update
11305          * Fires when a Record has been updated
11306          * @param {Store} this
11307          * @param {Roo.data.Record} record The Record that was updated
11308          * @param {String} operation The update operation being performed.  Value may be one of:
11309          * <pre><code>
11310  Roo.data.Record.EDIT
11311  Roo.data.Record.REJECT
11312  Roo.data.Record.COMMIT
11313          * </code></pre>
11314          */
11315         update : true,
11316         /**
11317          * @event clear
11318          * Fires when the data cache has been cleared.
11319          * @param {Store} this
11320          */
11321         clear : true,
11322         /**
11323          * @event beforeload
11324          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11325          * the load action will be canceled.
11326          * @param {Store} this
11327          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11328          */
11329         beforeload : true,
11330         /**
11331          * @event beforeloadadd
11332          * Fires after a new set of Records has been loaded.
11333          * @param {Store} this
11334          * @param {Roo.data.Record[]} records The Records that were loaded
11335          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11336          */
11337         beforeloadadd : true,
11338         /**
11339          * @event load
11340          * Fires after a new set of Records has been loaded, before they are added to the store.
11341          * @param {Store} this
11342          * @param {Roo.data.Record[]} records The Records that were loaded
11343          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11344          * @params {Object} return from reader
11345          */
11346         load : true,
11347         /**
11348          * @event loadexception
11349          * Fires if an exception occurs in the Proxy during loading.
11350          * Called with the signature of the Proxy's "loadexception" event.
11351          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11352          * 
11353          * @param {Proxy} 
11354          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11355          * @param {Object} load options 
11356          * @param {Object} jsonData from your request (normally this contains the Exception)
11357          */
11358         loadexception : true
11359     });
11360     
11361     if(this.proxy){
11362         this.proxy = Roo.factory(this.proxy, Roo.data);
11363         this.proxy.xmodule = this.xmodule || false;
11364         this.relayEvents(this.proxy,  ["loadexception"]);
11365     }
11366     this.sortToggle = {};
11367     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11368
11369     Roo.data.Store.superclass.constructor.call(this);
11370
11371     if(this.inlineData){
11372         this.loadData(this.inlineData);
11373         delete this.inlineData;
11374     }
11375 };
11376
11377 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11378      /**
11379     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11380     * without a remote query - used by combo/forms at present.
11381     */
11382     
11383     /**
11384     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11385     */
11386     /**
11387     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11388     */
11389     /**
11390     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11391     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11392     */
11393     /**
11394     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11395     * on any HTTP request
11396     */
11397     /**
11398     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11399     */
11400     /**
11401     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11402     */
11403     multiSort: false,
11404     /**
11405     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11406     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11407     */
11408     remoteSort : false,
11409
11410     /**
11411     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11412      * loaded or when a record is removed. (defaults to false).
11413     */
11414     pruneModifiedRecords : false,
11415
11416     // private
11417     lastOptions : null,
11418
11419     /**
11420      * Add Records to the Store and fires the add event.
11421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11422      */
11423     add : function(records){
11424         records = [].concat(records);
11425         for(var i = 0, len = records.length; i < len; i++){
11426             records[i].join(this);
11427         }
11428         var index = this.data.length;
11429         this.data.addAll(records);
11430         this.fireEvent("add", this, records, index);
11431     },
11432
11433     /**
11434      * Remove a Record from the Store and fires the remove event.
11435      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11436      */
11437     remove : function(record){
11438         var index = this.data.indexOf(record);
11439         this.data.removeAt(index);
11440  
11441         if(this.pruneModifiedRecords){
11442             this.modified.remove(record);
11443         }
11444         this.fireEvent("remove", this, record, index);
11445     },
11446
11447     /**
11448      * Remove all Records from the Store and fires the clear event.
11449      */
11450     removeAll : function(){
11451         this.data.clear();
11452         if(this.pruneModifiedRecords){
11453             this.modified = [];
11454         }
11455         this.fireEvent("clear", this);
11456     },
11457
11458     /**
11459      * Inserts Records to the Store at the given index and fires the add event.
11460      * @param {Number} index The start index at which to insert the passed Records.
11461      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11462      */
11463     insert : function(index, records){
11464         records = [].concat(records);
11465         for(var i = 0, len = records.length; i < len; i++){
11466             this.data.insert(index, records[i]);
11467             records[i].join(this);
11468         }
11469         this.fireEvent("add", this, records, index);
11470     },
11471
11472     /**
11473      * Get the index within the cache of the passed Record.
11474      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11475      * @return {Number} The index of the passed Record. Returns -1 if not found.
11476      */
11477     indexOf : function(record){
11478         return this.data.indexOf(record);
11479     },
11480
11481     /**
11482      * Get the index within the cache of the Record with the passed id.
11483      * @param {String} id The id of the Record to find.
11484      * @return {Number} The index of the Record. Returns -1 if not found.
11485      */
11486     indexOfId : function(id){
11487         return this.data.indexOfKey(id);
11488     },
11489
11490     /**
11491      * Get the Record with the specified id.
11492      * @param {String} id The id of the Record to find.
11493      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11494      */
11495     getById : function(id){
11496         return this.data.key(id);
11497     },
11498
11499     /**
11500      * Get the Record at the specified index.
11501      * @param {Number} index The index of the Record to find.
11502      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11503      */
11504     getAt : function(index){
11505         return this.data.itemAt(index);
11506     },
11507
11508     /**
11509      * Returns a range of Records between specified indices.
11510      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11511      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11512      * @return {Roo.data.Record[]} An array of Records
11513      */
11514     getRange : function(start, end){
11515         return this.data.getRange(start, end);
11516     },
11517
11518     // private
11519     storeOptions : function(o){
11520         o = Roo.apply({}, o);
11521         delete o.callback;
11522         delete o.scope;
11523         this.lastOptions = o;
11524     },
11525
11526     /**
11527      * Loads the Record cache from the configured Proxy using the configured Reader.
11528      * <p>
11529      * If using remote paging, then the first load call must specify the <em>start</em>
11530      * and <em>limit</em> properties in the options.params property to establish the initial
11531      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11532      * <p>
11533      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11534      * and this call will return before the new data has been loaded. Perform any post-processing
11535      * in a callback function, or in a "load" event handler.</strong>
11536      * <p>
11537      * @param {Object} options An object containing properties which control loading options:<ul>
11538      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11539      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11540      * passed the following arguments:<ul>
11541      * <li>r : Roo.data.Record[]</li>
11542      * <li>options: Options object from the load call</li>
11543      * <li>success: Boolean success indicator</li></ul></li>
11544      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11545      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11546      * </ul>
11547      */
11548     load : function(options){
11549         options = options || {};
11550         if(this.fireEvent("beforeload", this, options) !== false){
11551             this.storeOptions(options);
11552             var p = Roo.apply(options.params || {}, this.baseParams);
11553             // if meta was not loaded from remote source.. try requesting it.
11554             if (!this.reader.metaFromRemote) {
11555                 p._requestMeta = 1;
11556             }
11557             if(this.sortInfo && this.remoteSort){
11558                 var pn = this.paramNames;
11559                 p[pn["sort"]] = this.sortInfo.field;
11560                 p[pn["dir"]] = this.sortInfo.direction;
11561             }
11562             if (this.multiSort) {
11563                 var pn = this.paramNames;
11564                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11565             }
11566             
11567             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11568         }
11569     },
11570
11571     /**
11572      * Reloads the Record cache from the configured Proxy using the configured Reader and
11573      * the options from the last load operation performed.
11574      * @param {Object} options (optional) An object containing properties which may override the options
11575      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11576      * the most recently used options are reused).
11577      */
11578     reload : function(options){
11579         this.load(Roo.applyIf(options||{}, this.lastOptions));
11580     },
11581
11582     // private
11583     // Called as a callback by the Reader during a load operation.
11584     loadRecords : function(o, options, success){
11585         if(!o || success === false){
11586             if(success !== false){
11587                 this.fireEvent("load", this, [], options, o);
11588             }
11589             if(options.callback){
11590                 options.callback.call(options.scope || this, [], options, false);
11591             }
11592             return;
11593         }
11594         // if data returned failure - throw an exception.
11595         if (o.success === false) {
11596             // show a message if no listener is registered.
11597             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11598                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11599             }
11600             // loadmask wil be hooked into this..
11601             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11602             return;
11603         }
11604         var r = o.records, t = o.totalRecords || r.length;
11605         
11606         this.fireEvent("beforeloadadd", this, r, options, o);
11607         
11608         if(!options || options.add !== true){
11609             if(this.pruneModifiedRecords){
11610                 this.modified = [];
11611             }
11612             for(var i = 0, len = r.length; i < len; i++){
11613                 r[i].join(this);
11614             }
11615             if(this.snapshot){
11616                 this.data = this.snapshot;
11617                 delete this.snapshot;
11618             }
11619             this.data.clear();
11620             this.data.addAll(r);
11621             this.totalLength = t;
11622             this.applySort();
11623             this.fireEvent("datachanged", this);
11624         }else{
11625             this.totalLength = Math.max(t, this.data.length+r.length);
11626             this.add(r);
11627         }
11628         
11629         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11630                 
11631             var e = new Roo.data.Record({});
11632
11633             e.set(this.parent.displayField, this.parent.emptyTitle);
11634             e.set(this.parent.valueField, '');
11635
11636             this.insert(0, e);
11637         }
11638             
11639         this.fireEvent("load", this, r, options, o);
11640         if(options.callback){
11641             options.callback.call(options.scope || this, r, options, true);
11642         }
11643     },
11644
11645
11646     /**
11647      * Loads data from a passed data block. A Reader which understands the format of the data
11648      * must have been configured in the constructor.
11649      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11650      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11651      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11652      */
11653     loadData : function(o, append){
11654         var r = this.reader.readRecords(o);
11655         this.loadRecords(r, {add: append}, true);
11656     },
11657
11658     /**
11659      * Gets the number of cached records.
11660      * <p>
11661      * <em>If using paging, this may not be the total size of the dataset. If the data object
11662      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11663      * the data set size</em>
11664      */
11665     getCount : function(){
11666         return this.data.length || 0;
11667     },
11668
11669     /**
11670      * Gets the total number of records in the dataset as returned by the server.
11671      * <p>
11672      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11673      * the dataset size</em>
11674      */
11675     getTotalCount : function(){
11676         return this.totalLength || 0;
11677     },
11678
11679     /**
11680      * Returns the sort state of the Store as an object with two properties:
11681      * <pre><code>
11682  field {String} The name of the field by which the Records are sorted
11683  direction {String} The sort order, "ASC" or "DESC"
11684      * </code></pre>
11685      */
11686     getSortState : function(){
11687         return this.sortInfo;
11688     },
11689
11690     // private
11691     applySort : function(){
11692         if(this.sortInfo && !this.remoteSort){
11693             var s = this.sortInfo, f = s.field;
11694             var st = this.fields.get(f).sortType;
11695             var fn = function(r1, r2){
11696                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11697                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11698             };
11699             this.data.sort(s.direction, fn);
11700             if(this.snapshot && this.snapshot != this.data){
11701                 this.snapshot.sort(s.direction, fn);
11702             }
11703         }
11704     },
11705
11706     /**
11707      * Sets the default sort column and order to be used by the next load operation.
11708      * @param {String} fieldName The name of the field to sort by.
11709      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11710      */
11711     setDefaultSort : function(field, dir){
11712         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11713     },
11714
11715     /**
11716      * Sort the Records.
11717      * If remote sorting is used, the sort is performed on the server, and the cache is
11718      * reloaded. If local sorting is used, the cache is sorted internally.
11719      * @param {String} fieldName The name of the field to sort by.
11720      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11721      */
11722     sort : function(fieldName, dir){
11723         var f = this.fields.get(fieldName);
11724         if(!dir){
11725             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11726             
11727             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11728                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11729             }else{
11730                 dir = f.sortDir;
11731             }
11732         }
11733         this.sortToggle[f.name] = dir;
11734         this.sortInfo = {field: f.name, direction: dir};
11735         if(!this.remoteSort){
11736             this.applySort();
11737             this.fireEvent("datachanged", this);
11738         }else{
11739             this.load(this.lastOptions);
11740         }
11741     },
11742
11743     /**
11744      * Calls the specified function for each of the Records in the cache.
11745      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11746      * Returning <em>false</em> aborts and exits the iteration.
11747      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11748      */
11749     each : function(fn, scope){
11750         this.data.each(fn, scope);
11751     },
11752
11753     /**
11754      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11755      * (e.g., during paging).
11756      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11757      */
11758     getModifiedRecords : function(){
11759         return this.modified;
11760     },
11761
11762     // private
11763     createFilterFn : function(property, value, anyMatch){
11764         if(!value.exec){ // not a regex
11765             value = String(value);
11766             if(value.length == 0){
11767                 return false;
11768             }
11769             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11770         }
11771         return function(r){
11772             return value.test(r.data[property]);
11773         };
11774     },
11775
11776     /**
11777      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11778      * @param {String} property A field on your records
11779      * @param {Number} start The record index to start at (defaults to 0)
11780      * @param {Number} end The last record index to include (defaults to length - 1)
11781      * @return {Number} The sum
11782      */
11783     sum : function(property, start, end){
11784         var rs = this.data.items, v = 0;
11785         start = start || 0;
11786         end = (end || end === 0) ? end : rs.length-1;
11787
11788         for(var i = start; i <= end; i++){
11789             v += (rs[i].data[property] || 0);
11790         }
11791         return v;
11792     },
11793
11794     /**
11795      * Filter the records by a specified property.
11796      * @param {String} field A field on your records
11797      * @param {String/RegExp} value Either a string that the field
11798      * should start with or a RegExp to test against the field
11799      * @param {Boolean} anyMatch True to match any part not just the beginning
11800      */
11801     filter : function(property, value, anyMatch){
11802         var fn = this.createFilterFn(property, value, anyMatch);
11803         return fn ? this.filterBy(fn) : this.clearFilter();
11804     },
11805
11806     /**
11807      * Filter by a function. The specified function will be called with each
11808      * record in this data source. If the function returns true the record is included,
11809      * otherwise it is filtered.
11810      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11811      * @param {Object} scope (optional) The scope of the function (defaults to this)
11812      */
11813     filterBy : function(fn, scope){
11814         this.snapshot = this.snapshot || this.data;
11815         this.data = this.queryBy(fn, scope||this);
11816         this.fireEvent("datachanged", this);
11817     },
11818
11819     /**
11820      * Query the records by a specified property.
11821      * @param {String} field A field on your records
11822      * @param {String/RegExp} value Either a string that the field
11823      * should start with or a RegExp to test against the field
11824      * @param {Boolean} anyMatch True to match any part not just the beginning
11825      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11826      */
11827     query : function(property, value, anyMatch){
11828         var fn = this.createFilterFn(property, value, anyMatch);
11829         return fn ? this.queryBy(fn) : this.data.clone();
11830     },
11831
11832     /**
11833      * Query by a function. The specified function will be called with each
11834      * record in this data source. If the function returns true the record is included
11835      * in the results.
11836      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11837      * @param {Object} scope (optional) The scope of the function (defaults to this)
11838       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11839      **/
11840     queryBy : function(fn, scope){
11841         var data = this.snapshot || this.data;
11842         return data.filterBy(fn, scope||this);
11843     },
11844
11845     /**
11846      * Collects unique values for a particular dataIndex from this store.
11847      * @param {String} dataIndex The property to collect
11848      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11849      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11850      * @return {Array} An array of the unique values
11851      **/
11852     collect : function(dataIndex, allowNull, bypassFilter){
11853         var d = (bypassFilter === true && this.snapshot) ?
11854                 this.snapshot.items : this.data.items;
11855         var v, sv, r = [], l = {};
11856         for(var i = 0, len = d.length; i < len; i++){
11857             v = d[i].data[dataIndex];
11858             sv = String(v);
11859             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11860                 l[sv] = true;
11861                 r[r.length] = v;
11862             }
11863         }
11864         return r;
11865     },
11866
11867     /**
11868      * Revert to a view of the Record cache with no filtering applied.
11869      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11870      */
11871     clearFilter : function(suppressEvent){
11872         if(this.snapshot && this.snapshot != this.data){
11873             this.data = this.snapshot;
11874             delete this.snapshot;
11875             if(suppressEvent !== true){
11876                 this.fireEvent("datachanged", this);
11877             }
11878         }
11879     },
11880
11881     // private
11882     afterEdit : function(record){
11883         if(this.modified.indexOf(record) == -1){
11884             this.modified.push(record);
11885         }
11886         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11887     },
11888     
11889     // private
11890     afterReject : function(record){
11891         this.modified.remove(record);
11892         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11893     },
11894
11895     // private
11896     afterCommit : function(record){
11897         this.modified.remove(record);
11898         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11899     },
11900
11901     /**
11902      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11903      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11904      */
11905     commitChanges : function(){
11906         var m = this.modified.slice(0);
11907         this.modified = [];
11908         for(var i = 0, len = m.length; i < len; i++){
11909             m[i].commit();
11910         }
11911     },
11912
11913     /**
11914      * Cancel outstanding changes on all changed records.
11915      */
11916     rejectChanges : function(){
11917         var m = this.modified.slice(0);
11918         this.modified = [];
11919         for(var i = 0, len = m.length; i < len; i++){
11920             m[i].reject();
11921         }
11922     },
11923
11924     onMetaChange : function(meta, rtype, o){
11925         this.recordType = rtype;
11926         this.fields = rtype.prototype.fields;
11927         delete this.snapshot;
11928         this.sortInfo = meta.sortInfo || this.sortInfo;
11929         this.modified = [];
11930         this.fireEvent('metachange', this, this.reader.meta);
11931     },
11932     
11933     moveIndex : function(data, type)
11934     {
11935         var index = this.indexOf(data);
11936         
11937         var newIndex = index + type;
11938         
11939         this.remove(data);
11940         
11941         this.insert(newIndex, data);
11942         
11943     }
11944 });/*
11945  * Based on:
11946  * Ext JS Library 1.1.1
11947  * Copyright(c) 2006-2007, Ext JS, LLC.
11948  *
11949  * Originally Released Under LGPL - original licence link has changed is not relivant.
11950  *
11951  * Fork - LGPL
11952  * <script type="text/javascript">
11953  */
11954
11955 /**
11956  * @class Roo.data.SimpleStore
11957  * @extends Roo.data.Store
11958  * Small helper class to make creating Stores from Array data easier.
11959  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11960  * @cfg {Array} fields An array of field definition objects, or field name strings.
11961  * @cfg {Array} data The multi-dimensional array of data
11962  * @constructor
11963  * @param {Object} config
11964  */
11965 Roo.data.SimpleStore = function(config){
11966     Roo.data.SimpleStore.superclass.constructor.call(this, {
11967         isLocal : true,
11968         reader: new Roo.data.ArrayReader({
11969                 id: config.id
11970             },
11971             Roo.data.Record.create(config.fields)
11972         ),
11973         proxy : new Roo.data.MemoryProxy(config.data)
11974     });
11975     this.load();
11976 };
11977 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11978  * Based on:
11979  * Ext JS Library 1.1.1
11980  * Copyright(c) 2006-2007, Ext JS, LLC.
11981  *
11982  * Originally Released Under LGPL - original licence link has changed is not relivant.
11983  *
11984  * Fork - LGPL
11985  * <script type="text/javascript">
11986  */
11987
11988 /**
11989 /**
11990  * @extends Roo.data.Store
11991  * @class Roo.data.JsonStore
11992  * Small helper class to make creating Stores for JSON data easier. <br/>
11993 <pre><code>
11994 var store = new Roo.data.JsonStore({
11995     url: 'get-images.php',
11996     root: 'images',
11997     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11998 });
11999 </code></pre>
12000  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12001  * JsonReader and HttpProxy (unless inline data is provided).</b>
12002  * @cfg {Array} fields An array of field definition objects, or field name strings.
12003  * @constructor
12004  * @param {Object} config
12005  */
12006 Roo.data.JsonStore = function(c){
12007     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12008         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12009         reader: new Roo.data.JsonReader(c, c.fields)
12010     }));
12011 };
12012 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12013  * Based on:
12014  * Ext JS Library 1.1.1
12015  * Copyright(c) 2006-2007, Ext JS, LLC.
12016  *
12017  * Originally Released Under LGPL - original licence link has changed is not relivant.
12018  *
12019  * Fork - LGPL
12020  * <script type="text/javascript">
12021  */
12022
12023  
12024 Roo.data.Field = function(config){
12025     if(typeof config == "string"){
12026         config = {name: config};
12027     }
12028     Roo.apply(this, config);
12029     
12030     if(!this.type){
12031         this.type = "auto";
12032     }
12033     
12034     var st = Roo.data.SortTypes;
12035     // named sortTypes are supported, here we look them up
12036     if(typeof this.sortType == "string"){
12037         this.sortType = st[this.sortType];
12038     }
12039     
12040     // set default sortType for strings and dates
12041     if(!this.sortType){
12042         switch(this.type){
12043             case "string":
12044                 this.sortType = st.asUCString;
12045                 break;
12046             case "date":
12047                 this.sortType = st.asDate;
12048                 break;
12049             default:
12050                 this.sortType = st.none;
12051         }
12052     }
12053
12054     // define once
12055     var stripRe = /[\$,%]/g;
12056
12057     // prebuilt conversion function for this field, instead of
12058     // switching every time we're reading a value
12059     if(!this.convert){
12060         var cv, dateFormat = this.dateFormat;
12061         switch(this.type){
12062             case "":
12063             case "auto":
12064             case undefined:
12065                 cv = function(v){ return v; };
12066                 break;
12067             case "string":
12068                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12069                 break;
12070             case "int":
12071                 cv = function(v){
12072                     return v !== undefined && v !== null && v !== '' ?
12073                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12074                     };
12075                 break;
12076             case "float":
12077                 cv = function(v){
12078                     return v !== undefined && v !== null && v !== '' ?
12079                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12080                     };
12081                 break;
12082             case "bool":
12083             case "boolean":
12084                 cv = function(v){ return v === true || v === "true" || v == 1; };
12085                 break;
12086             case "date":
12087                 cv = function(v){
12088                     if(!v){
12089                         return '';
12090                     }
12091                     if(v instanceof Date){
12092                         return v;
12093                     }
12094                     if(dateFormat){
12095                         if(dateFormat == "timestamp"){
12096                             return new Date(v*1000);
12097                         }
12098                         return Date.parseDate(v, dateFormat);
12099                     }
12100                     var parsed = Date.parse(v);
12101                     return parsed ? new Date(parsed) : null;
12102                 };
12103              break;
12104             
12105         }
12106         this.convert = cv;
12107     }
12108 };
12109
12110 Roo.data.Field.prototype = {
12111     dateFormat: null,
12112     defaultValue: "",
12113     mapping: null,
12114     sortType : null,
12115     sortDir : "ASC"
12116 };/*
12117  * Based on:
12118  * Ext JS Library 1.1.1
12119  * Copyright(c) 2006-2007, Ext JS, LLC.
12120  *
12121  * Originally Released Under LGPL - original licence link has changed is not relivant.
12122  *
12123  * Fork - LGPL
12124  * <script type="text/javascript">
12125  */
12126  
12127 // Base class for reading structured data from a data source.  This class is intended to be
12128 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12129
12130 /**
12131  * @class Roo.data.DataReader
12132  * Base class for reading structured data from a data source.  This class is intended to be
12133  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12134  */
12135
12136 Roo.data.DataReader = function(meta, recordType){
12137     
12138     this.meta = meta;
12139     
12140     this.recordType = recordType instanceof Array ? 
12141         Roo.data.Record.create(recordType) : recordType;
12142 };
12143
12144 Roo.data.DataReader.prototype = {
12145      /**
12146      * Create an empty record
12147      * @param {Object} data (optional) - overlay some values
12148      * @return {Roo.data.Record} record created.
12149      */
12150     newRow :  function(d) {
12151         var da =  {};
12152         this.recordType.prototype.fields.each(function(c) {
12153             switch( c.type) {
12154                 case 'int' : da[c.name] = 0; break;
12155                 case 'date' : da[c.name] = new Date(); break;
12156                 case 'float' : da[c.name] = 0.0; break;
12157                 case 'boolean' : da[c.name] = false; break;
12158                 default : da[c.name] = ""; break;
12159             }
12160             
12161         });
12162         return new this.recordType(Roo.apply(da, d));
12163     }
12164     
12165 };/*
12166  * Based on:
12167  * Ext JS Library 1.1.1
12168  * Copyright(c) 2006-2007, Ext JS, LLC.
12169  *
12170  * Originally Released Under LGPL - original licence link has changed is not relivant.
12171  *
12172  * Fork - LGPL
12173  * <script type="text/javascript">
12174  */
12175
12176 /**
12177  * @class Roo.data.DataProxy
12178  * @extends Roo.data.Observable
12179  * This class is an abstract base class for implementations which provide retrieval of
12180  * unformatted data objects.<br>
12181  * <p>
12182  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12183  * (of the appropriate type which knows how to parse the data object) to provide a block of
12184  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12185  * <p>
12186  * Custom implementations must implement the load method as described in
12187  * {@link Roo.data.HttpProxy#load}.
12188  */
12189 Roo.data.DataProxy = function(){
12190     this.addEvents({
12191         /**
12192          * @event beforeload
12193          * Fires before a network request is made to retrieve a data object.
12194          * @param {Object} This DataProxy object.
12195          * @param {Object} params The params parameter to the load function.
12196          */
12197         beforeload : true,
12198         /**
12199          * @event load
12200          * Fires before the load method's callback is called.
12201          * @param {Object} This DataProxy object.
12202          * @param {Object} o The data object.
12203          * @param {Object} arg The callback argument object passed to the load function.
12204          */
12205         load : true,
12206         /**
12207          * @event loadexception
12208          * Fires if an Exception occurs during data retrieval.
12209          * @param {Object} This DataProxy object.
12210          * @param {Object} o The data object.
12211          * @param {Object} arg The callback argument object passed to the load function.
12212          * @param {Object} e The Exception.
12213          */
12214         loadexception : true
12215     });
12216     Roo.data.DataProxy.superclass.constructor.call(this);
12217 };
12218
12219 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12220
12221     /**
12222      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12223      */
12224 /*
12225  * Based on:
12226  * Ext JS Library 1.1.1
12227  * Copyright(c) 2006-2007, Ext JS, LLC.
12228  *
12229  * Originally Released Under LGPL - original licence link has changed is not relivant.
12230  *
12231  * Fork - LGPL
12232  * <script type="text/javascript">
12233  */
12234 /**
12235  * @class Roo.data.MemoryProxy
12236  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12237  * to the Reader when its load method is called.
12238  * @constructor
12239  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12240  */
12241 Roo.data.MemoryProxy = function(data){
12242     if (data.data) {
12243         data = data.data;
12244     }
12245     Roo.data.MemoryProxy.superclass.constructor.call(this);
12246     this.data = data;
12247 };
12248
12249 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12250     
12251     /**
12252      * Load data from the requested source (in this case an in-memory
12253      * data object passed to the constructor), read the data object into
12254      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12255      * process that block using the passed callback.
12256      * @param {Object} params This parameter is not used by the MemoryProxy class.
12257      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12258      * object into a block of Roo.data.Records.
12259      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12260      * The function must be passed <ul>
12261      * <li>The Record block object</li>
12262      * <li>The "arg" argument from the load function</li>
12263      * <li>A boolean success indicator</li>
12264      * </ul>
12265      * @param {Object} scope The scope in which to call the callback
12266      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12267      */
12268     load : function(params, reader, callback, scope, arg){
12269         params = params || {};
12270         var result;
12271         try {
12272             result = reader.readRecords(this.data);
12273         }catch(e){
12274             this.fireEvent("loadexception", this, arg, null, e);
12275             callback.call(scope, null, arg, false);
12276             return;
12277         }
12278         callback.call(scope, result, arg, true);
12279     },
12280     
12281     // private
12282     update : function(params, records){
12283         
12284     }
12285 });/*
12286  * Based on:
12287  * Ext JS Library 1.1.1
12288  * Copyright(c) 2006-2007, Ext JS, LLC.
12289  *
12290  * Originally Released Under LGPL - original licence link has changed is not relivant.
12291  *
12292  * Fork - LGPL
12293  * <script type="text/javascript">
12294  */
12295 /**
12296  * @class Roo.data.HttpProxy
12297  * @extends Roo.data.DataProxy
12298  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12299  * configured to reference a certain URL.<br><br>
12300  * <p>
12301  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12302  * from which the running page was served.<br><br>
12303  * <p>
12304  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12305  * <p>
12306  * Be aware that to enable the browser to parse an XML document, the server must set
12307  * the Content-Type header in the HTTP response to "text/xml".
12308  * @constructor
12309  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12310  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12311  * will be used to make the request.
12312  */
12313 Roo.data.HttpProxy = function(conn){
12314     Roo.data.HttpProxy.superclass.constructor.call(this);
12315     // is conn a conn config or a real conn?
12316     this.conn = conn;
12317     this.useAjax = !conn || !conn.events;
12318   
12319 };
12320
12321 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12322     // thse are take from connection...
12323     
12324     /**
12325      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12326      */
12327     /**
12328      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12329      * extra parameters to each request made by this object. (defaults to undefined)
12330      */
12331     /**
12332      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12333      *  to each request made by this object. (defaults to undefined)
12334      */
12335     /**
12336      * @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)
12337      */
12338     /**
12339      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12340      */
12341      /**
12342      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12343      * @type Boolean
12344      */
12345   
12346
12347     /**
12348      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12349      * @type Boolean
12350      */
12351     /**
12352      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12353      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12354      * a finer-grained basis than the DataProxy events.
12355      */
12356     getConnection : function(){
12357         return this.useAjax ? Roo.Ajax : this.conn;
12358     },
12359
12360     /**
12361      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12362      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12363      * process that block using the passed callback.
12364      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12365      * for the request to the remote server.
12366      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12367      * object into a block of Roo.data.Records.
12368      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12369      * The function must be passed <ul>
12370      * <li>The Record block object</li>
12371      * <li>The "arg" argument from the load function</li>
12372      * <li>A boolean success indicator</li>
12373      * </ul>
12374      * @param {Object} scope The scope in which to call the callback
12375      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12376      */
12377     load : function(params, reader, callback, scope, arg){
12378         if(this.fireEvent("beforeload", this, params) !== false){
12379             var  o = {
12380                 params : params || {},
12381                 request: {
12382                     callback : callback,
12383                     scope : scope,
12384                     arg : arg
12385                 },
12386                 reader: reader,
12387                 callback : this.loadResponse,
12388                 scope: this
12389             };
12390             if(this.useAjax){
12391                 Roo.applyIf(o, this.conn);
12392                 if(this.activeRequest){
12393                     Roo.Ajax.abort(this.activeRequest);
12394                 }
12395                 this.activeRequest = Roo.Ajax.request(o);
12396             }else{
12397                 this.conn.request(o);
12398             }
12399         }else{
12400             callback.call(scope||this, null, arg, false);
12401         }
12402     },
12403
12404     // private
12405     loadResponse : function(o, success, response){
12406         delete this.activeRequest;
12407         if(!success){
12408             this.fireEvent("loadexception", this, o, response);
12409             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12410             return;
12411         }
12412         var result;
12413         try {
12414             result = o.reader.read(response);
12415         }catch(e){
12416             this.fireEvent("loadexception", this, o, response, e);
12417             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12418             return;
12419         }
12420         
12421         this.fireEvent("load", this, o, o.request.arg);
12422         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12423     },
12424
12425     // private
12426     update : function(dataSet){
12427
12428     },
12429
12430     // private
12431     updateResponse : function(dataSet){
12432
12433     }
12434 });/*
12435  * Based on:
12436  * Ext JS Library 1.1.1
12437  * Copyright(c) 2006-2007, Ext JS, LLC.
12438  *
12439  * Originally Released Under LGPL - original licence link has changed is not relivant.
12440  *
12441  * Fork - LGPL
12442  * <script type="text/javascript">
12443  */
12444
12445 /**
12446  * @class Roo.data.ScriptTagProxy
12447  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12448  * other than the originating domain of the running page.<br><br>
12449  * <p>
12450  * <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
12451  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12452  * <p>
12453  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12454  * source code that is used as the source inside a &lt;script> tag.<br><br>
12455  * <p>
12456  * In order for the browser to process the returned data, the server must wrap the data object
12457  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12458  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12459  * depending on whether the callback name was passed:
12460  * <p>
12461  * <pre><code>
12462 boolean scriptTag = false;
12463 String cb = request.getParameter("callback");
12464 if (cb != null) {
12465     scriptTag = true;
12466     response.setContentType("text/javascript");
12467 } else {
12468     response.setContentType("application/x-json");
12469 }
12470 Writer out = response.getWriter();
12471 if (scriptTag) {
12472     out.write(cb + "(");
12473 }
12474 out.print(dataBlock.toJsonString());
12475 if (scriptTag) {
12476     out.write(");");
12477 }
12478 </pre></code>
12479  *
12480  * @constructor
12481  * @param {Object} config A configuration object.
12482  */
12483 Roo.data.ScriptTagProxy = function(config){
12484     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12485     Roo.apply(this, config);
12486     this.head = document.getElementsByTagName("head")[0];
12487 };
12488
12489 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12490
12491 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12492     /**
12493      * @cfg {String} url The URL from which to request the data object.
12494      */
12495     /**
12496      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12497      */
12498     timeout : 30000,
12499     /**
12500      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12501      * the server the name of the callback function set up by the load call to process the returned data object.
12502      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12503      * javascript output which calls this named function passing the data object as its only parameter.
12504      */
12505     callbackParam : "callback",
12506     /**
12507      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12508      * name to the request.
12509      */
12510     nocache : true,
12511
12512     /**
12513      * Load data from the configured URL, read the data object into
12514      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12515      * process that block using the passed callback.
12516      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12517      * for the request to the remote server.
12518      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12519      * object into a block of Roo.data.Records.
12520      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12521      * The function must be passed <ul>
12522      * <li>The Record block object</li>
12523      * <li>The "arg" argument from the load function</li>
12524      * <li>A boolean success indicator</li>
12525      * </ul>
12526      * @param {Object} scope The scope in which to call the callback
12527      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12528      */
12529     load : function(params, reader, callback, scope, arg){
12530         if(this.fireEvent("beforeload", this, params) !== false){
12531
12532             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12533
12534             var url = this.url;
12535             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12536             if(this.nocache){
12537                 url += "&_dc=" + (new Date().getTime());
12538             }
12539             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12540             var trans = {
12541                 id : transId,
12542                 cb : "stcCallback"+transId,
12543                 scriptId : "stcScript"+transId,
12544                 params : params,
12545                 arg : arg,
12546                 url : url,
12547                 callback : callback,
12548                 scope : scope,
12549                 reader : reader
12550             };
12551             var conn = this;
12552
12553             window[trans.cb] = function(o){
12554                 conn.handleResponse(o, trans);
12555             };
12556
12557             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12558
12559             if(this.autoAbort !== false){
12560                 this.abort();
12561             }
12562
12563             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12564
12565             var script = document.createElement("script");
12566             script.setAttribute("src", url);
12567             script.setAttribute("type", "text/javascript");
12568             script.setAttribute("id", trans.scriptId);
12569             this.head.appendChild(script);
12570
12571             this.trans = trans;
12572         }else{
12573             callback.call(scope||this, null, arg, false);
12574         }
12575     },
12576
12577     // private
12578     isLoading : function(){
12579         return this.trans ? true : false;
12580     },
12581
12582     /**
12583      * Abort the current server request.
12584      */
12585     abort : function(){
12586         if(this.isLoading()){
12587             this.destroyTrans(this.trans);
12588         }
12589     },
12590
12591     // private
12592     destroyTrans : function(trans, isLoaded){
12593         this.head.removeChild(document.getElementById(trans.scriptId));
12594         clearTimeout(trans.timeoutId);
12595         if(isLoaded){
12596             window[trans.cb] = undefined;
12597             try{
12598                 delete window[trans.cb];
12599             }catch(e){}
12600         }else{
12601             // if hasn't been loaded, wait for load to remove it to prevent script error
12602             window[trans.cb] = function(){
12603                 window[trans.cb] = undefined;
12604                 try{
12605                     delete window[trans.cb];
12606                 }catch(e){}
12607             };
12608         }
12609     },
12610
12611     // private
12612     handleResponse : function(o, trans){
12613         this.trans = false;
12614         this.destroyTrans(trans, true);
12615         var result;
12616         try {
12617             result = trans.reader.readRecords(o);
12618         }catch(e){
12619             this.fireEvent("loadexception", this, o, trans.arg, e);
12620             trans.callback.call(trans.scope||window, null, trans.arg, false);
12621             return;
12622         }
12623         this.fireEvent("load", this, o, trans.arg);
12624         trans.callback.call(trans.scope||window, result, trans.arg, true);
12625     },
12626
12627     // private
12628     handleFailure : function(trans){
12629         this.trans = false;
12630         this.destroyTrans(trans, false);
12631         this.fireEvent("loadexception", this, null, trans.arg);
12632         trans.callback.call(trans.scope||window, null, trans.arg, false);
12633     }
12634 });/*
12635  * Based on:
12636  * Ext JS Library 1.1.1
12637  * Copyright(c) 2006-2007, Ext JS, LLC.
12638  *
12639  * Originally Released Under LGPL - original licence link has changed is not relivant.
12640  *
12641  * Fork - LGPL
12642  * <script type="text/javascript">
12643  */
12644
12645 /**
12646  * @class Roo.data.JsonReader
12647  * @extends Roo.data.DataReader
12648  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12649  * based on mappings in a provided Roo.data.Record constructor.
12650  * 
12651  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12652  * in the reply previously. 
12653  * 
12654  * <p>
12655  * Example code:
12656  * <pre><code>
12657 var RecordDef = Roo.data.Record.create([
12658     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12659     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12660 ]);
12661 var myReader = new Roo.data.JsonReader({
12662     totalProperty: "results",    // The property which contains the total dataset size (optional)
12663     root: "rows",                // The property which contains an Array of row objects
12664     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12665 }, RecordDef);
12666 </code></pre>
12667  * <p>
12668  * This would consume a JSON file like this:
12669  * <pre><code>
12670 { 'results': 2, 'rows': [
12671     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12672     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12673 }
12674 </code></pre>
12675  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12676  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12677  * paged from the remote server.
12678  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12679  * @cfg {String} root name of the property which contains the Array of row objects.
12680  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12681  * @cfg {Array} fields Array of field definition objects
12682  * @constructor
12683  * Create a new JsonReader
12684  * @param {Object} meta Metadata configuration options
12685  * @param {Object} recordType Either an Array of field definition objects,
12686  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12687  */
12688 Roo.data.JsonReader = function(meta, recordType){
12689     
12690     meta = meta || {};
12691     // set some defaults:
12692     Roo.applyIf(meta, {
12693         totalProperty: 'total',
12694         successProperty : 'success',
12695         root : 'data',
12696         id : 'id'
12697     });
12698     
12699     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12700 };
12701 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12702     
12703     /**
12704      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12705      * Used by Store query builder to append _requestMeta to params.
12706      * 
12707      */
12708     metaFromRemote : false,
12709     /**
12710      * This method is only used by a DataProxy which has retrieved data from a remote server.
12711      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12712      * @return {Object} data A data block which is used by an Roo.data.Store object as
12713      * a cache of Roo.data.Records.
12714      */
12715     read : function(response){
12716         var json = response.responseText;
12717        
12718         var o = /* eval:var:o */ eval("("+json+")");
12719         if(!o) {
12720             throw {message: "JsonReader.read: Json object not found"};
12721         }
12722         
12723         if(o.metaData){
12724             
12725             delete this.ef;
12726             this.metaFromRemote = true;
12727             this.meta = o.metaData;
12728             this.recordType = Roo.data.Record.create(o.metaData.fields);
12729             this.onMetaChange(this.meta, this.recordType, o);
12730         }
12731         return this.readRecords(o);
12732     },
12733
12734     // private function a store will implement
12735     onMetaChange : function(meta, recordType, o){
12736
12737     },
12738
12739     /**
12740          * @ignore
12741          */
12742     simpleAccess: function(obj, subsc) {
12743         return obj[subsc];
12744     },
12745
12746         /**
12747          * @ignore
12748          */
12749     getJsonAccessor: function(){
12750         var re = /[\[\.]/;
12751         return function(expr) {
12752             try {
12753                 return(re.test(expr))
12754                     ? new Function("obj", "return obj." + expr)
12755                     : function(obj){
12756                         return obj[expr];
12757                     };
12758             } catch(e){}
12759             return Roo.emptyFn;
12760         };
12761     }(),
12762
12763     /**
12764      * Create a data block containing Roo.data.Records from an XML document.
12765      * @param {Object} o An object which contains an Array of row objects in the property specified
12766      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12767      * which contains the total size of the dataset.
12768      * @return {Object} data A data block which is used by an Roo.data.Store object as
12769      * a cache of Roo.data.Records.
12770      */
12771     readRecords : function(o){
12772         /**
12773          * After any data loads, the raw JSON data is available for further custom processing.
12774          * @type Object
12775          */
12776         this.o = o;
12777         var s = this.meta, Record = this.recordType,
12778             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12779
12780 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12781         if (!this.ef) {
12782             if(s.totalProperty) {
12783                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12784                 }
12785                 if(s.successProperty) {
12786                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12787                 }
12788                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12789                 if (s.id) {
12790                         var g = this.getJsonAccessor(s.id);
12791                         this.getId = function(rec) {
12792                                 var r = g(rec);  
12793                                 return (r === undefined || r === "") ? null : r;
12794                         };
12795                 } else {
12796                         this.getId = function(){return null;};
12797                 }
12798             this.ef = [];
12799             for(var jj = 0; jj < fl; jj++){
12800                 f = fi[jj];
12801                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12802                 this.ef[jj] = this.getJsonAccessor(map);
12803             }
12804         }
12805
12806         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12807         if(s.totalProperty){
12808             var vt = parseInt(this.getTotal(o), 10);
12809             if(!isNaN(vt)){
12810                 totalRecords = vt;
12811             }
12812         }
12813         if(s.successProperty){
12814             var vs = this.getSuccess(o);
12815             if(vs === false || vs === 'false'){
12816                 success = false;
12817             }
12818         }
12819         var records = [];
12820         for(var i = 0; i < c; i++){
12821                 var n = root[i];
12822             var values = {};
12823             var id = this.getId(n);
12824             for(var j = 0; j < fl; j++){
12825                 f = fi[j];
12826             var v = this.ef[j](n);
12827             if (!f.convert) {
12828                 Roo.log('missing convert for ' + f.name);
12829                 Roo.log(f);
12830                 continue;
12831             }
12832             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12833             }
12834             var record = new Record(values, id);
12835             record.json = n;
12836             records[i] = record;
12837         }
12838         return {
12839             raw : o,
12840             success : success,
12841             records : records,
12842             totalRecords : totalRecords
12843         };
12844     }
12845 });/*
12846  * Based on:
12847  * Ext JS Library 1.1.1
12848  * Copyright(c) 2006-2007, Ext JS, LLC.
12849  *
12850  * Originally Released Under LGPL - original licence link has changed is not relivant.
12851  *
12852  * Fork - LGPL
12853  * <script type="text/javascript">
12854  */
12855
12856 /**
12857  * @class Roo.data.ArrayReader
12858  * @extends Roo.data.DataReader
12859  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12860  * Each element of that Array represents a row of data fields. The
12861  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12862  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12863  * <p>
12864  * Example code:.
12865  * <pre><code>
12866 var RecordDef = Roo.data.Record.create([
12867     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12868     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12869 ]);
12870 var myReader = new Roo.data.ArrayReader({
12871     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12872 }, RecordDef);
12873 </code></pre>
12874  * <p>
12875  * This would consume an Array like this:
12876  * <pre><code>
12877 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12878   </code></pre>
12879  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12880  * @constructor
12881  * Create a new JsonReader
12882  * @param {Object} meta Metadata configuration options.
12883  * @param {Object} recordType Either an Array of field definition objects
12884  * as specified to {@link Roo.data.Record#create},
12885  * or an {@link Roo.data.Record} object
12886  * created using {@link Roo.data.Record#create}.
12887  */
12888 Roo.data.ArrayReader = function(meta, recordType){
12889     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12890 };
12891
12892 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12893     /**
12894      * Create a data block containing Roo.data.Records from an XML document.
12895      * @param {Object} o An Array of row objects which represents the dataset.
12896      * @return {Object} data A data block which is used by an Roo.data.Store object as
12897      * a cache of Roo.data.Records.
12898      */
12899     readRecords : function(o){
12900         var sid = this.meta ? this.meta.id : null;
12901         var recordType = this.recordType, fields = recordType.prototype.fields;
12902         var records = [];
12903         var root = o;
12904             for(var i = 0; i < root.length; i++){
12905                     var n = root[i];
12906                 var values = {};
12907                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12908                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12909                 var f = fields.items[j];
12910                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12911                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12912                 v = f.convert(v);
12913                 values[f.name] = v;
12914             }
12915                 var record = new recordType(values, id);
12916                 record.json = n;
12917                 records[records.length] = record;
12918             }
12919             return {
12920                 records : records,
12921                 totalRecords : records.length
12922             };
12923     }
12924 });/*
12925  * - LGPL
12926  * * 
12927  */
12928
12929 /**
12930  * @class Roo.bootstrap.ComboBox
12931  * @extends Roo.bootstrap.TriggerField
12932  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12933  * @cfg {Boolean} append (true|false) default false
12934  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12935  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12936  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12937  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12938  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12939  * @cfg {Boolean} animate default true
12940  * @cfg {Boolean} emptyResultText only for touch device
12941  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12942  * @cfg {String} emptyTitle default ''
12943  * @constructor
12944  * Create a new ComboBox.
12945  * @param {Object} config Configuration options
12946  */
12947 Roo.bootstrap.ComboBox = function(config){
12948     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12949     this.addEvents({
12950         /**
12951          * @event expand
12952          * Fires when the dropdown list is expanded
12953         * @param {Roo.bootstrap.ComboBox} combo This combo box
12954         */
12955         'expand' : true,
12956         /**
12957          * @event collapse
12958          * Fires when the dropdown list is collapsed
12959         * @param {Roo.bootstrap.ComboBox} combo This combo box
12960         */
12961         'collapse' : true,
12962         /**
12963          * @event beforeselect
12964          * Fires before a list item is selected. Return false to cancel the selection.
12965         * @param {Roo.bootstrap.ComboBox} combo This combo box
12966         * @param {Roo.data.Record} record The data record returned from the underlying store
12967         * @param {Number} index The index of the selected item in the dropdown list
12968         */
12969         'beforeselect' : true,
12970         /**
12971          * @event select
12972          * Fires when a list item is selected
12973         * @param {Roo.bootstrap.ComboBox} combo This combo box
12974         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12975         * @param {Number} index The index of the selected item in the dropdown list
12976         */
12977         'select' : true,
12978         /**
12979          * @event beforequery
12980          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12981          * The event object passed has these properties:
12982         * @param {Roo.bootstrap.ComboBox} combo This combo box
12983         * @param {String} query The query
12984         * @param {Boolean} forceAll true to force "all" query
12985         * @param {Boolean} cancel true to cancel the query
12986         * @param {Object} e The query event object
12987         */
12988         'beforequery': true,
12989          /**
12990          * @event add
12991          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12992         * @param {Roo.bootstrap.ComboBox} combo This combo box
12993         */
12994         'add' : true,
12995         /**
12996          * @event edit
12997          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12998         * @param {Roo.bootstrap.ComboBox} combo This combo box
12999         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13000         */
13001         'edit' : true,
13002         /**
13003          * @event remove
13004          * Fires when the remove value from the combobox array
13005         * @param {Roo.bootstrap.ComboBox} combo This combo box
13006         */
13007         'remove' : true,
13008         /**
13009          * @event afterremove
13010          * Fires when the remove value from the combobox array
13011         * @param {Roo.bootstrap.ComboBox} combo This combo box
13012         */
13013         'afterremove' : true,
13014         /**
13015          * @event specialfilter
13016          * Fires when specialfilter
13017             * @param {Roo.bootstrap.ComboBox} combo This combo box
13018             */
13019         'specialfilter' : true,
13020         /**
13021          * @event tick
13022          * Fires when tick the element
13023             * @param {Roo.bootstrap.ComboBox} combo This combo box
13024             */
13025         'tick' : true,
13026         /**
13027          * @event touchviewdisplay
13028          * Fires when touch view require special display (default is using displayField)
13029             * @param {Roo.bootstrap.ComboBox} combo This combo box
13030             * @param {Object} cfg set html .
13031             */
13032         'touchviewdisplay' : true
13033         
13034     });
13035     
13036     this.item = [];
13037     this.tickItems = [];
13038     
13039     this.selectedIndex = -1;
13040     if(this.mode == 'local'){
13041         if(config.queryDelay === undefined){
13042             this.queryDelay = 10;
13043         }
13044         if(config.minChars === undefined){
13045             this.minChars = 0;
13046         }
13047     }
13048 };
13049
13050 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13051      
13052     /**
13053      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13054      * rendering into an Roo.Editor, defaults to false)
13055      */
13056     /**
13057      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13058      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13059      */
13060     /**
13061      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13062      */
13063     /**
13064      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13065      * the dropdown list (defaults to undefined, with no header element)
13066      */
13067
13068      /**
13069      * @cfg {String/Roo.Template} tpl The template to use to render the output
13070      */
13071      
13072      /**
13073      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13074      */
13075     listWidth: undefined,
13076     /**
13077      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13078      * mode = 'remote' or 'text' if mode = 'local')
13079      */
13080     displayField: undefined,
13081     
13082     /**
13083      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13084      * mode = 'remote' or 'value' if mode = 'local'). 
13085      * Note: use of a valueField requires the user make a selection
13086      * in order for a value to be mapped.
13087      */
13088     valueField: undefined,
13089     /**
13090      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13091      */
13092     modalTitle : '',
13093     
13094     /**
13095      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13096      * field's data value (defaults to the underlying DOM element's name)
13097      */
13098     hiddenName: undefined,
13099     /**
13100      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13101      */
13102     listClass: '',
13103     /**
13104      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13105      */
13106     selectedClass: 'active',
13107     
13108     /**
13109      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13110      */
13111     shadow:'sides',
13112     /**
13113      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13114      * anchor positions (defaults to 'tl-bl')
13115      */
13116     listAlign: 'tl-bl?',
13117     /**
13118      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13119      */
13120     maxHeight: 300,
13121     /**
13122      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13123      * query specified by the allQuery config option (defaults to 'query')
13124      */
13125     triggerAction: 'query',
13126     /**
13127      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13128      * (defaults to 4, does not apply if editable = false)
13129      */
13130     minChars : 4,
13131     /**
13132      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13133      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13134      */
13135     typeAhead: false,
13136     /**
13137      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13138      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13139      */
13140     queryDelay: 500,
13141     /**
13142      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13143      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13144      */
13145     pageSize: 0,
13146     /**
13147      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13148      * when editable = true (defaults to false)
13149      */
13150     selectOnFocus:false,
13151     /**
13152      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13153      */
13154     queryParam: 'query',
13155     /**
13156      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13157      * when mode = 'remote' (defaults to 'Loading...')
13158      */
13159     loadingText: 'Loading...',
13160     /**
13161      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13162      */
13163     resizable: false,
13164     /**
13165      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13166      */
13167     handleHeight : 8,
13168     /**
13169      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13170      * traditional select (defaults to true)
13171      */
13172     editable: true,
13173     /**
13174      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13175      */
13176     allQuery: '',
13177     /**
13178      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13179      */
13180     mode: 'remote',
13181     /**
13182      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13183      * listWidth has a higher value)
13184      */
13185     minListWidth : 70,
13186     /**
13187      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13188      * allow the user to set arbitrary text into the field (defaults to false)
13189      */
13190     forceSelection:false,
13191     /**
13192      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13193      * if typeAhead = true (defaults to 250)
13194      */
13195     typeAheadDelay : 250,
13196     /**
13197      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13198      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13199      */
13200     valueNotFoundText : undefined,
13201     /**
13202      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13203      */
13204     blockFocus : false,
13205     
13206     /**
13207      * @cfg {Boolean} disableClear Disable showing of clear button.
13208      */
13209     disableClear : false,
13210     /**
13211      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13212      */
13213     alwaysQuery : false,
13214     
13215     /**
13216      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13217      */
13218     multiple : false,
13219     
13220     /**
13221      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13222      */
13223     invalidClass : "has-warning",
13224     
13225     /**
13226      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13227      */
13228     validClass : "has-success",
13229     
13230     /**
13231      * @cfg {Boolean} specialFilter (true|false) special filter default false
13232      */
13233     specialFilter : false,
13234     
13235     /**
13236      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13237      */
13238     mobileTouchView : true,
13239     
13240     /**
13241      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13242      */
13243     useNativeIOS : false,
13244     
13245     /**
13246      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13247      */
13248     mobile_restrict_height : false,
13249     
13250     ios_options : false,
13251     
13252     //private
13253     addicon : false,
13254     editicon: false,
13255     
13256     page: 0,
13257     hasQuery: false,
13258     append: false,
13259     loadNext: false,
13260     autoFocus : true,
13261     tickable : false,
13262     btnPosition : 'right',
13263     triggerList : true,
13264     showToggleBtn : true,
13265     animate : true,
13266     emptyResultText: 'Empty',
13267     triggerText : 'Select',
13268     emptyTitle : '',
13269     
13270     // element that contains real text value.. (when hidden is used..)
13271     
13272     getAutoCreate : function()
13273     {   
13274         var cfg = false;
13275         //render
13276         /*
13277          * Render classic select for iso
13278          */
13279         
13280         if(Roo.isIOS && this.useNativeIOS){
13281             cfg = this.getAutoCreateNativeIOS();
13282             return cfg;
13283         }
13284         
13285         /*
13286          * Touch Devices
13287          */
13288         
13289         if(Roo.isTouch && this.mobileTouchView){
13290             cfg = this.getAutoCreateTouchView();
13291             return cfg;;
13292         }
13293         
13294         /*
13295          *  Normal ComboBox
13296          */
13297         if(!this.tickable){
13298             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13299             return cfg;
13300         }
13301         
13302         /*
13303          *  ComboBox with tickable selections
13304          */
13305              
13306         var align = this.labelAlign || this.parentLabelAlign();
13307         
13308         cfg = {
13309             cls : 'form-group roo-combobox-tickable' //input-group
13310         };
13311         
13312         var btn_text_select = '';
13313         var btn_text_done = '';
13314         var btn_text_cancel = '';
13315         
13316         if (this.btn_text_show) {
13317             btn_text_select = 'Select';
13318             btn_text_done = 'Done';
13319             btn_text_cancel = 'Cancel'; 
13320         }
13321         
13322         var buttons = {
13323             tag : 'div',
13324             cls : 'tickable-buttons',
13325             cn : [
13326                 {
13327                     tag : 'button',
13328                     type : 'button',
13329                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13330                     //html : this.triggerText
13331                     html: btn_text_select
13332                 },
13333                 {
13334                     tag : 'button',
13335                     type : 'button',
13336                     name : 'ok',
13337                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13338                     //html : 'Done'
13339                     html: btn_text_done
13340                 },
13341                 {
13342                     tag : 'button',
13343                     type : 'button',
13344                     name : 'cancel',
13345                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13346                     //html : 'Cancel'
13347                     html: btn_text_cancel
13348                 }
13349             ]
13350         };
13351         
13352         if(this.editable){
13353             buttons.cn.unshift({
13354                 tag: 'input',
13355                 cls: 'roo-select2-search-field-input'
13356             });
13357         }
13358         
13359         var _this = this;
13360         
13361         Roo.each(buttons.cn, function(c){
13362             if (_this.size) {
13363                 c.cls += ' btn-' + _this.size;
13364             }
13365
13366             if (_this.disabled) {
13367                 c.disabled = true;
13368             }
13369         });
13370         
13371         var box = {
13372             tag: 'div',
13373             cn: [
13374                 {
13375                     tag: 'input',
13376                     type : 'hidden',
13377                     cls: 'form-hidden-field'
13378                 },
13379                 {
13380                     tag: 'ul',
13381                     cls: 'roo-select2-choices',
13382                     cn:[
13383                         {
13384                             tag: 'li',
13385                             cls: 'roo-select2-search-field',
13386                             cn: [
13387                                 buttons
13388                             ]
13389                         }
13390                     ]
13391                 }
13392             ]
13393         };
13394         
13395         var combobox = {
13396             cls: 'roo-select2-container input-group roo-select2-container-multi',
13397             cn: [
13398                 
13399                 box
13400 //                {
13401 //                    tag: 'ul',
13402 //                    cls: 'typeahead typeahead-long dropdown-menu',
13403 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13404 //                }
13405             ]
13406         };
13407         
13408         if(this.hasFeedback && !this.allowBlank){
13409             
13410             var feedback = {
13411                 tag: 'span',
13412                 cls: 'glyphicon form-control-feedback'
13413             };
13414
13415             combobox.cn.push(feedback);
13416         }
13417         
13418         var indicator = {
13419             tag : 'i',
13420             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13421             tooltip : 'This field is required'
13422         };
13423         if (Roo.bootstrap.version == 4) {
13424             indicator = {
13425                 tag : 'i',
13426                 style : 'display:none'
13427             };
13428         }
13429         if (align ==='left' && this.fieldLabel.length) {
13430             
13431             cfg.cls += ' roo-form-group-label-left row';
13432             
13433             cfg.cn = [
13434                 indicator,
13435                 {
13436                     tag: 'label',
13437                     'for' :  id,
13438                     cls : 'control-label col-form-label',
13439                     html : this.fieldLabel
13440
13441                 },
13442                 {
13443                     cls : "", 
13444                     cn: [
13445                         combobox
13446                     ]
13447                 }
13448
13449             ];
13450             
13451             var labelCfg = cfg.cn[1];
13452             var contentCfg = cfg.cn[2];
13453             
13454
13455             if(this.indicatorpos == 'right'){
13456                 
13457                 cfg.cn = [
13458                     {
13459                         tag: 'label',
13460                         'for' :  id,
13461                         cls : 'control-label col-form-label',
13462                         cn : [
13463                             {
13464                                 tag : 'span',
13465                                 html : this.fieldLabel
13466                             },
13467                             indicator
13468                         ]
13469                     },
13470                     {
13471                         cls : "",
13472                         cn: [
13473                             combobox
13474                         ]
13475                     }
13476
13477                 ];
13478                 
13479                 
13480                 
13481                 labelCfg = cfg.cn[0];
13482                 contentCfg = cfg.cn[1];
13483             
13484             }
13485             
13486             if(this.labelWidth > 12){
13487                 labelCfg.style = "width: " + this.labelWidth + 'px';
13488             }
13489             
13490             if(this.labelWidth < 13 && this.labelmd == 0){
13491                 this.labelmd = this.labelWidth;
13492             }
13493             
13494             if(this.labellg > 0){
13495                 labelCfg.cls += ' col-lg-' + this.labellg;
13496                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13497             }
13498             
13499             if(this.labelmd > 0){
13500                 labelCfg.cls += ' col-md-' + this.labelmd;
13501                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13502             }
13503             
13504             if(this.labelsm > 0){
13505                 labelCfg.cls += ' col-sm-' + this.labelsm;
13506                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13507             }
13508             
13509             if(this.labelxs > 0){
13510                 labelCfg.cls += ' col-xs-' + this.labelxs;
13511                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13512             }
13513                 
13514                 
13515         } else if ( this.fieldLabel.length) {
13516 //                Roo.log(" label");
13517                  cfg.cn = [
13518                    indicator,
13519                     {
13520                         tag: 'label',
13521                         //cls : 'input-group-addon',
13522                         html : this.fieldLabel
13523                     },
13524                     combobox
13525                 ];
13526                 
13527                 if(this.indicatorpos == 'right'){
13528                     cfg.cn = [
13529                         {
13530                             tag: 'label',
13531                             //cls : 'input-group-addon',
13532                             html : this.fieldLabel
13533                         },
13534                         indicator,
13535                         combobox
13536                     ];
13537                     
13538                 }
13539
13540         } else {
13541             
13542 //                Roo.log(" no label && no align");
13543                 cfg = combobox
13544                      
13545                 
13546         }
13547          
13548         var settings=this;
13549         ['xs','sm','md','lg'].map(function(size){
13550             if (settings[size]) {
13551                 cfg.cls += ' col-' + size + '-' + settings[size];
13552             }
13553         });
13554         
13555         return cfg;
13556         
13557     },
13558     
13559     _initEventsCalled : false,
13560     
13561     // private
13562     initEvents: function()
13563     {   
13564         if (this._initEventsCalled) { // as we call render... prevent looping...
13565             return;
13566         }
13567         this._initEventsCalled = true;
13568         
13569         if (!this.store) {
13570             throw "can not find store for combo";
13571         }
13572         
13573         this.indicator = this.indicatorEl();
13574         
13575         this.store = Roo.factory(this.store, Roo.data);
13576         this.store.parent = this;
13577         
13578         // if we are building from html. then this element is so complex, that we can not really
13579         // use the rendered HTML.
13580         // so we have to trash and replace the previous code.
13581         if (Roo.XComponent.build_from_html) {
13582             // remove this element....
13583             var e = this.el.dom, k=0;
13584             while (e ) { e = e.previousSibling;  ++k;}
13585
13586             this.el.remove();
13587             
13588             this.el=false;
13589             this.rendered = false;
13590             
13591             this.render(this.parent().getChildContainer(true), k);
13592         }
13593         
13594         if(Roo.isIOS && this.useNativeIOS){
13595             this.initIOSView();
13596             return;
13597         }
13598         
13599         /*
13600          * Touch Devices
13601          */
13602         
13603         if(Roo.isTouch && this.mobileTouchView){
13604             this.initTouchView();
13605             return;
13606         }
13607         
13608         if(this.tickable){
13609             this.initTickableEvents();
13610             return;
13611         }
13612         
13613         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13614         
13615         if(this.hiddenName){
13616             
13617             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13618             
13619             this.hiddenField.dom.value =
13620                 this.hiddenValue !== undefined ? this.hiddenValue :
13621                 this.value !== undefined ? this.value : '';
13622
13623             // prevent input submission
13624             this.el.dom.removeAttribute('name');
13625             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13626              
13627              
13628         }
13629         //if(Roo.isGecko){
13630         //    this.el.dom.setAttribute('autocomplete', 'off');
13631         //}
13632         
13633         var cls = 'x-combo-list';
13634         
13635         //this.list = new Roo.Layer({
13636         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13637         //});
13638         
13639         var _this = this;
13640         
13641         (function(){
13642             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13643             _this.list.setWidth(lw);
13644         }).defer(100);
13645         
13646         this.list.on('mouseover', this.onViewOver, this);
13647         this.list.on('mousemove', this.onViewMove, this);
13648         this.list.on('scroll', this.onViewScroll, this);
13649         
13650         /*
13651         this.list.swallowEvent('mousewheel');
13652         this.assetHeight = 0;
13653
13654         if(this.title){
13655             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13656             this.assetHeight += this.header.getHeight();
13657         }
13658
13659         this.innerList = this.list.createChild({cls:cls+'-inner'});
13660         this.innerList.on('mouseover', this.onViewOver, this);
13661         this.innerList.on('mousemove', this.onViewMove, this);
13662         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13663         
13664         if(this.allowBlank && !this.pageSize && !this.disableClear){
13665             this.footer = this.list.createChild({cls:cls+'-ft'});
13666             this.pageTb = new Roo.Toolbar(this.footer);
13667            
13668         }
13669         if(this.pageSize){
13670             this.footer = this.list.createChild({cls:cls+'-ft'});
13671             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13672                     {pageSize: this.pageSize});
13673             
13674         }
13675         
13676         if (this.pageTb && this.allowBlank && !this.disableClear) {
13677             var _this = this;
13678             this.pageTb.add(new Roo.Toolbar.Fill(), {
13679                 cls: 'x-btn-icon x-btn-clear',
13680                 text: '&#160;',
13681                 handler: function()
13682                 {
13683                     _this.collapse();
13684                     _this.clearValue();
13685                     _this.onSelect(false, -1);
13686                 }
13687             });
13688         }
13689         if (this.footer) {
13690             this.assetHeight += this.footer.getHeight();
13691         }
13692         */
13693             
13694         if(!this.tpl){
13695             this.tpl = Roo.bootstrap.version == 4 ?
13696                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13697                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13698         }
13699
13700         this.view = new Roo.View(this.list, this.tpl, {
13701             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13702         });
13703         //this.view.wrapEl.setDisplayed(false);
13704         this.view.on('click', this.onViewClick, this);
13705         
13706         
13707         this.store.on('beforeload', this.onBeforeLoad, this);
13708         this.store.on('load', this.onLoad, this);
13709         this.store.on('loadexception', this.onLoadException, this);
13710         /*
13711         if(this.resizable){
13712             this.resizer = new Roo.Resizable(this.list,  {
13713                pinned:true, handles:'se'
13714             });
13715             this.resizer.on('resize', function(r, w, h){
13716                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13717                 this.listWidth = w;
13718                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13719                 this.restrictHeight();
13720             }, this);
13721             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13722         }
13723         */
13724         if(!this.editable){
13725             this.editable = true;
13726             this.setEditable(false);
13727         }
13728         
13729         /*
13730         
13731         if (typeof(this.events.add.listeners) != 'undefined') {
13732             
13733             this.addicon = this.wrap.createChild(
13734                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13735        
13736             this.addicon.on('click', function(e) {
13737                 this.fireEvent('add', this);
13738             }, this);
13739         }
13740         if (typeof(this.events.edit.listeners) != 'undefined') {
13741             
13742             this.editicon = this.wrap.createChild(
13743                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13744             if (this.addicon) {
13745                 this.editicon.setStyle('margin-left', '40px');
13746             }
13747             this.editicon.on('click', function(e) {
13748                 
13749                 // we fire even  if inothing is selected..
13750                 this.fireEvent('edit', this, this.lastData );
13751                 
13752             }, this);
13753         }
13754         */
13755         
13756         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13757             "up" : function(e){
13758                 this.inKeyMode = true;
13759                 this.selectPrev();
13760             },
13761
13762             "down" : function(e){
13763                 if(!this.isExpanded()){
13764                     this.onTriggerClick();
13765                 }else{
13766                     this.inKeyMode = true;
13767                     this.selectNext();
13768                 }
13769             },
13770
13771             "enter" : function(e){
13772 //                this.onViewClick();
13773                 //return true;
13774                 this.collapse();
13775                 
13776                 if(this.fireEvent("specialkey", this, e)){
13777                     this.onViewClick(false);
13778                 }
13779                 
13780                 return true;
13781             },
13782
13783             "esc" : function(e){
13784                 this.collapse();
13785             },
13786
13787             "tab" : function(e){
13788                 this.collapse();
13789                 
13790                 if(this.fireEvent("specialkey", this, e)){
13791                     this.onViewClick(false);
13792                 }
13793                 
13794                 return true;
13795             },
13796
13797             scope : this,
13798
13799             doRelay : function(foo, bar, hname){
13800                 if(hname == 'down' || this.scope.isExpanded()){
13801                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13802                 }
13803                 return true;
13804             },
13805
13806             forceKeyDown: true
13807         });
13808         
13809         
13810         this.queryDelay = Math.max(this.queryDelay || 10,
13811                 this.mode == 'local' ? 10 : 250);
13812         
13813         
13814         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13815         
13816         if(this.typeAhead){
13817             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13818         }
13819         if(this.editable !== false){
13820             this.inputEl().on("keyup", this.onKeyUp, this);
13821         }
13822         if(this.forceSelection){
13823             this.inputEl().on('blur', this.doForce, this);
13824         }
13825         
13826         if(this.multiple){
13827             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13828             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13829         }
13830     },
13831     
13832     initTickableEvents: function()
13833     {   
13834         this.createList();
13835         
13836         if(this.hiddenName){
13837             
13838             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13839             
13840             this.hiddenField.dom.value =
13841                 this.hiddenValue !== undefined ? this.hiddenValue :
13842                 this.value !== undefined ? this.value : '';
13843
13844             // prevent input submission
13845             this.el.dom.removeAttribute('name');
13846             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13847              
13848              
13849         }
13850         
13851 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13852         
13853         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13854         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13855         if(this.triggerList){
13856             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13857         }
13858          
13859         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13860         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13861         
13862         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13863         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13864         
13865         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13866         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13867         
13868         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13869         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13870         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13871         
13872         this.okBtn.hide();
13873         this.cancelBtn.hide();
13874         
13875         var _this = this;
13876         
13877         (function(){
13878             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13879             _this.list.setWidth(lw);
13880         }).defer(100);
13881         
13882         this.list.on('mouseover', this.onViewOver, this);
13883         this.list.on('mousemove', this.onViewMove, this);
13884         
13885         this.list.on('scroll', this.onViewScroll, this);
13886         
13887         if(!this.tpl){
13888             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13889                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13890         }
13891
13892         this.view = new Roo.View(this.list, this.tpl, {
13893             singleSelect:true,
13894             tickable:true,
13895             parent:this,
13896             store: this.store,
13897             selectedClass: this.selectedClass
13898         });
13899         
13900         //this.view.wrapEl.setDisplayed(false);
13901         this.view.on('click', this.onViewClick, this);
13902         
13903         
13904         
13905         this.store.on('beforeload', this.onBeforeLoad, this);
13906         this.store.on('load', this.onLoad, this);
13907         this.store.on('loadexception', this.onLoadException, this);
13908         
13909         if(this.editable){
13910             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13911                 "up" : function(e){
13912                     this.inKeyMode = true;
13913                     this.selectPrev();
13914                 },
13915
13916                 "down" : function(e){
13917                     this.inKeyMode = true;
13918                     this.selectNext();
13919                 },
13920
13921                 "enter" : function(e){
13922                     if(this.fireEvent("specialkey", this, e)){
13923                         this.onViewClick(false);
13924                     }
13925                     
13926                     return true;
13927                 },
13928
13929                 "esc" : function(e){
13930                     this.onTickableFooterButtonClick(e, false, false);
13931                 },
13932
13933                 "tab" : function(e){
13934                     this.fireEvent("specialkey", this, e);
13935                     
13936                     this.onTickableFooterButtonClick(e, false, false);
13937                     
13938                     return true;
13939                 },
13940
13941                 scope : this,
13942
13943                 doRelay : function(e, fn, key){
13944                     if(this.scope.isExpanded()){
13945                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13946                     }
13947                     return true;
13948                 },
13949
13950                 forceKeyDown: true
13951             });
13952         }
13953         
13954         this.queryDelay = Math.max(this.queryDelay || 10,
13955                 this.mode == 'local' ? 10 : 250);
13956         
13957         
13958         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13959         
13960         if(this.typeAhead){
13961             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13962         }
13963         
13964         if(this.editable !== false){
13965             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13966         }
13967         
13968         this.indicator = this.indicatorEl();
13969         
13970         if(this.indicator){
13971             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13972             this.indicator.hide();
13973         }
13974         
13975     },
13976
13977     onDestroy : function(){
13978         if(this.view){
13979             this.view.setStore(null);
13980             this.view.el.removeAllListeners();
13981             this.view.el.remove();
13982             this.view.purgeListeners();
13983         }
13984         if(this.list){
13985             this.list.dom.innerHTML  = '';
13986         }
13987         
13988         if(this.store){
13989             this.store.un('beforeload', this.onBeforeLoad, this);
13990             this.store.un('load', this.onLoad, this);
13991             this.store.un('loadexception', this.onLoadException, this);
13992         }
13993         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13994     },
13995
13996     // private
13997     fireKey : function(e){
13998         if(e.isNavKeyPress() && !this.list.isVisible()){
13999             this.fireEvent("specialkey", this, e);
14000         }
14001     },
14002
14003     // private
14004     onResize: function(w, h){
14005 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14006 //        
14007 //        if(typeof w != 'number'){
14008 //            // we do not handle it!?!?
14009 //            return;
14010 //        }
14011 //        var tw = this.trigger.getWidth();
14012 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14013 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14014 //        var x = w - tw;
14015 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14016 //            
14017 //        //this.trigger.setStyle('left', x+'px');
14018 //        
14019 //        if(this.list && this.listWidth === undefined){
14020 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14021 //            this.list.setWidth(lw);
14022 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14023 //        }
14024         
14025     
14026         
14027     },
14028
14029     /**
14030      * Allow or prevent the user from directly editing the field text.  If false is passed,
14031      * the user will only be able to select from the items defined in the dropdown list.  This method
14032      * is the runtime equivalent of setting the 'editable' config option at config time.
14033      * @param {Boolean} value True to allow the user to directly edit the field text
14034      */
14035     setEditable : function(value){
14036         if(value == this.editable){
14037             return;
14038         }
14039         this.editable = value;
14040         if(!value){
14041             this.inputEl().dom.setAttribute('readOnly', true);
14042             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14043             this.inputEl().addClass('x-combo-noedit');
14044         }else{
14045             this.inputEl().dom.setAttribute('readOnly', false);
14046             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14047             this.inputEl().removeClass('x-combo-noedit');
14048         }
14049     },
14050
14051     // private
14052     
14053     onBeforeLoad : function(combo,opts){
14054         if(!this.hasFocus){
14055             return;
14056         }
14057          if (!opts.add) {
14058             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14059          }
14060         this.restrictHeight();
14061         this.selectedIndex = -1;
14062     },
14063
14064     // private
14065     onLoad : function(){
14066         
14067         this.hasQuery = false;
14068         
14069         if(!this.hasFocus){
14070             return;
14071         }
14072         
14073         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14074             this.loading.hide();
14075         }
14076         
14077         if(this.store.getCount() > 0){
14078             
14079             this.expand();
14080             this.restrictHeight();
14081             if(this.lastQuery == this.allQuery){
14082                 if(this.editable && !this.tickable){
14083                     this.inputEl().dom.select();
14084                 }
14085                 
14086                 if(
14087                     !this.selectByValue(this.value, true) &&
14088                     this.autoFocus && 
14089                     (
14090                         !this.store.lastOptions ||
14091                         typeof(this.store.lastOptions.add) == 'undefined' || 
14092                         this.store.lastOptions.add != true
14093                     )
14094                 ){
14095                     this.select(0, true);
14096                 }
14097             }else{
14098                 if(this.autoFocus){
14099                     this.selectNext();
14100                 }
14101                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14102                     this.taTask.delay(this.typeAheadDelay);
14103                 }
14104             }
14105         }else{
14106             this.onEmptyResults();
14107         }
14108         
14109         //this.el.focus();
14110     },
14111     // private
14112     onLoadException : function()
14113     {
14114         this.hasQuery = false;
14115         
14116         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14117             this.loading.hide();
14118         }
14119         
14120         if(this.tickable && this.editable){
14121             return;
14122         }
14123         
14124         this.collapse();
14125         // only causes errors at present
14126         //Roo.log(this.store.reader.jsonData);
14127         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14128             // fixme
14129             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14130         //}
14131         
14132         
14133     },
14134     // private
14135     onTypeAhead : function(){
14136         if(this.store.getCount() > 0){
14137             var r = this.store.getAt(0);
14138             var newValue = r.data[this.displayField];
14139             var len = newValue.length;
14140             var selStart = this.getRawValue().length;
14141             
14142             if(selStart != len){
14143                 this.setRawValue(newValue);
14144                 this.selectText(selStart, newValue.length);
14145             }
14146         }
14147     },
14148
14149     // private
14150     onSelect : function(record, index){
14151         
14152         if(this.fireEvent('beforeselect', this, record, index) !== false){
14153         
14154             this.setFromData(index > -1 ? record.data : false);
14155             
14156             this.collapse();
14157             this.fireEvent('select', this, record, index);
14158         }
14159     },
14160
14161     /**
14162      * Returns the currently selected field value or empty string if no value is set.
14163      * @return {String} value The selected value
14164      */
14165     getValue : function()
14166     {
14167         if(Roo.isIOS && this.useNativeIOS){
14168             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14169         }
14170         
14171         if(this.multiple){
14172             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14173         }
14174         
14175         if(this.valueField){
14176             return typeof this.value != 'undefined' ? this.value : '';
14177         }else{
14178             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14179         }
14180     },
14181     
14182     getRawValue : function()
14183     {
14184         if(Roo.isIOS && this.useNativeIOS){
14185             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14186         }
14187         
14188         var v = this.inputEl().getValue();
14189         
14190         return v;
14191     },
14192
14193     /**
14194      * Clears any text/value currently set in the field
14195      */
14196     clearValue : function(){
14197         
14198         if(this.hiddenField){
14199             this.hiddenField.dom.value = '';
14200         }
14201         this.value = '';
14202         this.setRawValue('');
14203         this.lastSelectionText = '';
14204         this.lastData = false;
14205         
14206         var close = this.closeTriggerEl();
14207         
14208         if(close){
14209             close.hide();
14210         }
14211         
14212         this.validate();
14213         
14214     },
14215
14216     /**
14217      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14218      * will be displayed in the field.  If the value does not match the data value of an existing item,
14219      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14220      * Otherwise the field will be blank (although the value will still be set).
14221      * @param {String} value The value to match
14222      */
14223     setValue : function(v)
14224     {
14225         if(Roo.isIOS && this.useNativeIOS){
14226             this.setIOSValue(v);
14227             return;
14228         }
14229         
14230         if(this.multiple){
14231             this.syncValue();
14232             return;
14233         }
14234         
14235         var text = v;
14236         if(this.valueField){
14237             var r = this.findRecord(this.valueField, v);
14238             if(r){
14239                 text = r.data[this.displayField];
14240             }else if(this.valueNotFoundText !== undefined){
14241                 text = this.valueNotFoundText;
14242             }
14243         }
14244         this.lastSelectionText = text;
14245         if(this.hiddenField){
14246             this.hiddenField.dom.value = v;
14247         }
14248         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14249         this.value = v;
14250         
14251         var close = this.closeTriggerEl();
14252         
14253         if(close){
14254             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14255         }
14256         
14257         this.validate();
14258     },
14259     /**
14260      * @property {Object} the last set data for the element
14261      */
14262     
14263     lastData : false,
14264     /**
14265      * Sets the value of the field based on a object which is related to the record format for the store.
14266      * @param {Object} value the value to set as. or false on reset?
14267      */
14268     setFromData : function(o){
14269         
14270         if(this.multiple){
14271             this.addItem(o);
14272             return;
14273         }
14274             
14275         var dv = ''; // display value
14276         var vv = ''; // value value..
14277         this.lastData = o;
14278         if (this.displayField) {
14279             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14280         } else {
14281             // this is an error condition!!!
14282             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14283         }
14284         
14285         if(this.valueField){
14286             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14287         }
14288         
14289         var close = this.closeTriggerEl();
14290         
14291         if(close){
14292             if(dv.length || vv * 1 > 0){
14293                 close.show() ;
14294                 this.blockFocus=true;
14295             } else {
14296                 close.hide();
14297             }             
14298         }
14299         
14300         if(this.hiddenField){
14301             this.hiddenField.dom.value = vv;
14302             
14303             this.lastSelectionText = dv;
14304             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14305             this.value = vv;
14306             return;
14307         }
14308         // no hidden field.. - we store the value in 'value', but still display
14309         // display field!!!!
14310         this.lastSelectionText = dv;
14311         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14312         this.value = vv;
14313         
14314         
14315         
14316     },
14317     // private
14318     reset : function(){
14319         // overridden so that last data is reset..
14320         
14321         if(this.multiple){
14322             this.clearItem();
14323             return;
14324         }
14325         
14326         this.setValue(this.originalValue);
14327         //this.clearInvalid();
14328         this.lastData = false;
14329         if (this.view) {
14330             this.view.clearSelections();
14331         }
14332         
14333         this.validate();
14334     },
14335     // private
14336     findRecord : function(prop, value){
14337         var record;
14338         if(this.store.getCount() > 0){
14339             this.store.each(function(r){
14340                 if(r.data[prop] == value){
14341                     record = r;
14342                     return false;
14343                 }
14344                 return true;
14345             });
14346         }
14347         return record;
14348     },
14349     
14350     getName: function()
14351     {
14352         // returns hidden if it's set..
14353         if (!this.rendered) {return ''};
14354         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14355         
14356     },
14357     // private
14358     onViewMove : function(e, t){
14359         this.inKeyMode = false;
14360     },
14361
14362     // private
14363     onViewOver : function(e, t){
14364         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14365             return;
14366         }
14367         var item = this.view.findItemFromChild(t);
14368         
14369         if(item){
14370             var index = this.view.indexOf(item);
14371             this.select(index, false);
14372         }
14373     },
14374
14375     // private
14376     onViewClick : function(view, doFocus, el, e)
14377     {
14378         var index = this.view.getSelectedIndexes()[0];
14379         
14380         var r = this.store.getAt(index);
14381         
14382         if(this.tickable){
14383             
14384             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14385                 return;
14386             }
14387             
14388             var rm = false;
14389             var _this = this;
14390             
14391             Roo.each(this.tickItems, function(v,k){
14392                 
14393                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14394                     Roo.log(v);
14395                     _this.tickItems.splice(k, 1);
14396                     
14397                     if(typeof(e) == 'undefined' && view == false){
14398                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14399                     }
14400                     
14401                     rm = true;
14402                     return;
14403                 }
14404             });
14405             
14406             if(rm){
14407                 return;
14408             }
14409             
14410             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14411                 this.tickItems.push(r.data);
14412             }
14413             
14414             if(typeof(e) == 'undefined' && view == false){
14415                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14416             }
14417                     
14418             return;
14419         }
14420         
14421         if(r){
14422             this.onSelect(r, index);
14423         }
14424         if(doFocus !== false && !this.blockFocus){
14425             this.inputEl().focus();
14426         }
14427     },
14428
14429     // private
14430     restrictHeight : function(){
14431         //this.innerList.dom.style.height = '';
14432         //var inner = this.innerList.dom;
14433         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14434         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14435         //this.list.beginUpdate();
14436         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14437         this.list.alignTo(this.inputEl(), this.listAlign);
14438         this.list.alignTo(this.inputEl(), this.listAlign);
14439         //this.list.endUpdate();
14440     },
14441
14442     // private
14443     onEmptyResults : function(){
14444         
14445         if(this.tickable && this.editable){
14446             this.hasFocus = false;
14447             this.restrictHeight();
14448             return;
14449         }
14450         
14451         this.collapse();
14452     },
14453
14454     /**
14455      * Returns true if the dropdown list is expanded, else false.
14456      */
14457     isExpanded : function(){
14458         return this.list.isVisible();
14459     },
14460
14461     /**
14462      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14463      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14464      * @param {String} value The data value of the item to select
14465      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14466      * selected item if it is not currently in view (defaults to true)
14467      * @return {Boolean} True if the value matched an item in the list, else false
14468      */
14469     selectByValue : function(v, scrollIntoView){
14470         if(v !== undefined && v !== null){
14471             var r = this.findRecord(this.valueField || this.displayField, v);
14472             if(r){
14473                 this.select(this.store.indexOf(r), scrollIntoView);
14474                 return true;
14475             }
14476         }
14477         return false;
14478     },
14479
14480     /**
14481      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14482      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14483      * @param {Number} index The zero-based index of the list item to select
14484      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14485      * selected item if it is not currently in view (defaults to true)
14486      */
14487     select : function(index, scrollIntoView){
14488         this.selectedIndex = index;
14489         this.view.select(index);
14490         if(scrollIntoView !== false){
14491             var el = this.view.getNode(index);
14492             /*
14493              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14494              */
14495             if(el){
14496                 this.list.scrollChildIntoView(el, false);
14497             }
14498         }
14499     },
14500
14501     // private
14502     selectNext : function(){
14503         var ct = this.store.getCount();
14504         if(ct > 0){
14505             if(this.selectedIndex == -1){
14506                 this.select(0);
14507             }else if(this.selectedIndex < ct-1){
14508                 this.select(this.selectedIndex+1);
14509             }
14510         }
14511     },
14512
14513     // private
14514     selectPrev : function(){
14515         var ct = this.store.getCount();
14516         if(ct > 0){
14517             if(this.selectedIndex == -1){
14518                 this.select(0);
14519             }else if(this.selectedIndex != 0){
14520                 this.select(this.selectedIndex-1);
14521             }
14522         }
14523     },
14524
14525     // private
14526     onKeyUp : function(e){
14527         if(this.editable !== false && !e.isSpecialKey()){
14528             this.lastKey = e.getKey();
14529             this.dqTask.delay(this.queryDelay);
14530         }
14531     },
14532
14533     // private
14534     validateBlur : function(){
14535         return !this.list || !this.list.isVisible();   
14536     },
14537
14538     // private
14539     initQuery : function(){
14540         
14541         var v = this.getRawValue();
14542         
14543         if(this.tickable && this.editable){
14544             v = this.tickableInputEl().getValue();
14545         }
14546         
14547         this.doQuery(v);
14548     },
14549
14550     // private
14551     doForce : function(){
14552         if(this.inputEl().dom.value.length > 0){
14553             this.inputEl().dom.value =
14554                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14555              
14556         }
14557     },
14558
14559     /**
14560      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14561      * query allowing the query action to be canceled if needed.
14562      * @param {String} query The SQL query to execute
14563      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14564      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14565      * saved in the current store (defaults to false)
14566      */
14567     doQuery : function(q, forceAll){
14568         
14569         if(q === undefined || q === null){
14570             q = '';
14571         }
14572         var qe = {
14573             query: q,
14574             forceAll: forceAll,
14575             combo: this,
14576             cancel:false
14577         };
14578         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14579             return false;
14580         }
14581         q = qe.query;
14582         
14583         forceAll = qe.forceAll;
14584         if(forceAll === true || (q.length >= this.minChars)){
14585             
14586             this.hasQuery = true;
14587             
14588             if(this.lastQuery != q || this.alwaysQuery){
14589                 this.lastQuery = q;
14590                 if(this.mode == 'local'){
14591                     this.selectedIndex = -1;
14592                     if(forceAll){
14593                         this.store.clearFilter();
14594                     }else{
14595                         
14596                         if(this.specialFilter){
14597                             this.fireEvent('specialfilter', this);
14598                             this.onLoad();
14599                             return;
14600                         }
14601                         
14602                         this.store.filter(this.displayField, q);
14603                     }
14604                     
14605                     this.store.fireEvent("datachanged", this.store);
14606                     
14607                     this.onLoad();
14608                     
14609                     
14610                 }else{
14611                     
14612                     this.store.baseParams[this.queryParam] = q;
14613                     
14614                     var options = {params : this.getParams(q)};
14615                     
14616                     if(this.loadNext){
14617                         options.add = true;
14618                         options.params.start = this.page * this.pageSize;
14619                     }
14620                     
14621                     this.store.load(options);
14622                     
14623                     /*
14624                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14625                      *  we should expand the list on onLoad
14626                      *  so command out it
14627                      */
14628 //                    this.expand();
14629                 }
14630             }else{
14631                 this.selectedIndex = -1;
14632                 this.onLoad();   
14633             }
14634         }
14635         
14636         this.loadNext = false;
14637     },
14638     
14639     // private
14640     getParams : function(q){
14641         var p = {};
14642         //p[this.queryParam] = q;
14643         
14644         if(this.pageSize){
14645             p.start = 0;
14646             p.limit = this.pageSize;
14647         }
14648         return p;
14649     },
14650
14651     /**
14652      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14653      */
14654     collapse : function(){
14655         if(!this.isExpanded()){
14656             return;
14657         }
14658         
14659         this.list.hide();
14660         
14661         this.hasFocus = false;
14662         
14663         if(this.tickable){
14664             this.okBtn.hide();
14665             this.cancelBtn.hide();
14666             this.trigger.show();
14667             
14668             if(this.editable){
14669                 this.tickableInputEl().dom.value = '';
14670                 this.tickableInputEl().blur();
14671             }
14672             
14673         }
14674         
14675         Roo.get(document).un('mousedown', this.collapseIf, this);
14676         Roo.get(document).un('mousewheel', this.collapseIf, this);
14677         if (!this.editable) {
14678             Roo.get(document).un('keydown', this.listKeyPress, this);
14679         }
14680         this.fireEvent('collapse', this);
14681         
14682         this.validate();
14683     },
14684
14685     // private
14686     collapseIf : function(e){
14687         var in_combo  = e.within(this.el);
14688         var in_list =  e.within(this.list);
14689         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14690         
14691         if (in_combo || in_list || is_list) {
14692             //e.stopPropagation();
14693             return;
14694         }
14695         
14696         if(this.tickable){
14697             this.onTickableFooterButtonClick(e, false, false);
14698         }
14699
14700         this.collapse();
14701         
14702     },
14703
14704     /**
14705      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14706      */
14707     expand : function(){
14708        
14709         if(this.isExpanded() || !this.hasFocus){
14710             return;
14711         }
14712         
14713         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14714         this.list.setWidth(lw);
14715         
14716         Roo.log('expand');
14717         
14718         this.list.show();
14719         
14720         this.restrictHeight();
14721         
14722         if(this.tickable){
14723             
14724             this.tickItems = Roo.apply([], this.item);
14725             
14726             this.okBtn.show();
14727             this.cancelBtn.show();
14728             this.trigger.hide();
14729             
14730             if(this.editable){
14731                 this.tickableInputEl().focus();
14732             }
14733             
14734         }
14735         
14736         Roo.get(document).on('mousedown', this.collapseIf, this);
14737         Roo.get(document).on('mousewheel', this.collapseIf, this);
14738         if (!this.editable) {
14739             Roo.get(document).on('keydown', this.listKeyPress, this);
14740         }
14741         
14742         this.fireEvent('expand', this);
14743     },
14744
14745     // private
14746     // Implements the default empty TriggerField.onTriggerClick function
14747     onTriggerClick : function(e)
14748     {
14749         Roo.log('trigger click');
14750         
14751         if(this.disabled || !this.triggerList){
14752             return;
14753         }
14754         
14755         this.page = 0;
14756         this.loadNext = false;
14757         
14758         if(this.isExpanded()){
14759             this.collapse();
14760             if (!this.blockFocus) {
14761                 this.inputEl().focus();
14762             }
14763             
14764         }else {
14765             this.hasFocus = true;
14766             if(this.triggerAction == 'all') {
14767                 this.doQuery(this.allQuery, true);
14768             } else {
14769                 this.doQuery(this.getRawValue());
14770             }
14771             if (!this.blockFocus) {
14772                 this.inputEl().focus();
14773             }
14774         }
14775     },
14776     
14777     onTickableTriggerClick : function(e)
14778     {
14779         if(this.disabled){
14780             return;
14781         }
14782         
14783         this.page = 0;
14784         this.loadNext = false;
14785         this.hasFocus = true;
14786         
14787         if(this.triggerAction == 'all') {
14788             this.doQuery(this.allQuery, true);
14789         } else {
14790             this.doQuery(this.getRawValue());
14791         }
14792     },
14793     
14794     onSearchFieldClick : function(e)
14795     {
14796         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14797             this.onTickableFooterButtonClick(e, false, false);
14798             return;
14799         }
14800         
14801         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14802             return;
14803         }
14804         
14805         this.page = 0;
14806         this.loadNext = false;
14807         this.hasFocus = true;
14808         
14809         if(this.triggerAction == 'all') {
14810             this.doQuery(this.allQuery, true);
14811         } else {
14812             this.doQuery(this.getRawValue());
14813         }
14814     },
14815     
14816     listKeyPress : function(e)
14817     {
14818         //Roo.log('listkeypress');
14819         // scroll to first matching element based on key pres..
14820         if (e.isSpecialKey()) {
14821             return false;
14822         }
14823         var k = String.fromCharCode(e.getKey()).toUpperCase();
14824         //Roo.log(k);
14825         var match  = false;
14826         var csel = this.view.getSelectedNodes();
14827         var cselitem = false;
14828         if (csel.length) {
14829             var ix = this.view.indexOf(csel[0]);
14830             cselitem  = this.store.getAt(ix);
14831             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14832                 cselitem = false;
14833             }
14834             
14835         }
14836         
14837         this.store.each(function(v) { 
14838             if (cselitem) {
14839                 // start at existing selection.
14840                 if (cselitem.id == v.id) {
14841                     cselitem = false;
14842                 }
14843                 return true;
14844             }
14845                 
14846             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14847                 match = this.store.indexOf(v);
14848                 return false;
14849             }
14850             return true;
14851         }, this);
14852         
14853         if (match === false) {
14854             return true; // no more action?
14855         }
14856         // scroll to?
14857         this.view.select(match);
14858         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14859         sn.scrollIntoView(sn.dom.parentNode, false);
14860     },
14861     
14862     onViewScroll : function(e, t){
14863         
14864         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){
14865             return;
14866         }
14867         
14868         this.hasQuery = true;
14869         
14870         this.loading = this.list.select('.loading', true).first();
14871         
14872         if(this.loading === null){
14873             this.list.createChild({
14874                 tag: 'div',
14875                 cls: 'loading roo-select2-more-results roo-select2-active',
14876                 html: 'Loading more results...'
14877             });
14878             
14879             this.loading = this.list.select('.loading', true).first();
14880             
14881             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14882             
14883             this.loading.hide();
14884         }
14885         
14886         this.loading.show();
14887         
14888         var _combo = this;
14889         
14890         this.page++;
14891         this.loadNext = true;
14892         
14893         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14894         
14895         return;
14896     },
14897     
14898     addItem : function(o)
14899     {   
14900         var dv = ''; // display value
14901         
14902         if (this.displayField) {
14903             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14904         } else {
14905             // this is an error condition!!!
14906             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14907         }
14908         
14909         if(!dv.length){
14910             return;
14911         }
14912         
14913         var choice = this.choices.createChild({
14914             tag: 'li',
14915             cls: 'roo-select2-search-choice',
14916             cn: [
14917                 {
14918                     tag: 'div',
14919                     html: dv
14920                 },
14921                 {
14922                     tag: 'a',
14923                     href: '#',
14924                     cls: 'roo-select2-search-choice-close fa fa-times',
14925                     tabindex: '-1'
14926                 }
14927             ]
14928             
14929         }, this.searchField);
14930         
14931         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14932         
14933         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14934         
14935         this.item.push(o);
14936         
14937         this.lastData = o;
14938         
14939         this.syncValue();
14940         
14941         this.inputEl().dom.value = '';
14942         
14943         this.validate();
14944     },
14945     
14946     onRemoveItem : function(e, _self, o)
14947     {
14948         e.preventDefault();
14949         
14950         this.lastItem = Roo.apply([], this.item);
14951         
14952         var index = this.item.indexOf(o.data) * 1;
14953         
14954         if( index < 0){
14955             Roo.log('not this item?!');
14956             return;
14957         }
14958         
14959         this.item.splice(index, 1);
14960         o.item.remove();
14961         
14962         this.syncValue();
14963         
14964         this.fireEvent('remove', this, e);
14965         
14966         this.validate();
14967         
14968     },
14969     
14970     syncValue : function()
14971     {
14972         if(!this.item.length){
14973             this.clearValue();
14974             return;
14975         }
14976             
14977         var value = [];
14978         var _this = this;
14979         Roo.each(this.item, function(i){
14980             if(_this.valueField){
14981                 value.push(i[_this.valueField]);
14982                 return;
14983             }
14984
14985             value.push(i);
14986         });
14987
14988         this.value = value.join(',');
14989
14990         if(this.hiddenField){
14991             this.hiddenField.dom.value = this.value;
14992         }
14993         
14994         this.store.fireEvent("datachanged", this.store);
14995         
14996         this.validate();
14997     },
14998     
14999     clearItem : function()
15000     {
15001         if(!this.multiple){
15002             return;
15003         }
15004         
15005         this.item = [];
15006         
15007         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15008            c.remove();
15009         });
15010         
15011         this.syncValue();
15012         
15013         this.validate();
15014         
15015         if(this.tickable && !Roo.isTouch){
15016             this.view.refresh();
15017         }
15018     },
15019     
15020     inputEl: function ()
15021     {
15022         if(Roo.isIOS && this.useNativeIOS){
15023             return this.el.select('select.roo-ios-select', true).first();
15024         }
15025         
15026         if(Roo.isTouch && this.mobileTouchView){
15027             return this.el.select('input.form-control',true).first();
15028         }
15029         
15030         if(this.tickable){
15031             return this.searchField;
15032         }
15033         
15034         return this.el.select('input.form-control',true).first();
15035     },
15036     
15037     onTickableFooterButtonClick : function(e, btn, el)
15038     {
15039         e.preventDefault();
15040         
15041         this.lastItem = Roo.apply([], this.item);
15042         
15043         if(btn && btn.name == 'cancel'){
15044             this.tickItems = Roo.apply([], this.item);
15045             this.collapse();
15046             return;
15047         }
15048         
15049         this.clearItem();
15050         
15051         var _this = this;
15052         
15053         Roo.each(this.tickItems, function(o){
15054             _this.addItem(o);
15055         });
15056         
15057         this.collapse();
15058         
15059     },
15060     
15061     validate : function()
15062     {
15063         if(this.getVisibilityEl().hasClass('hidden')){
15064             return true;
15065         }
15066         
15067         var v = this.getRawValue();
15068         
15069         if(this.multiple){
15070             v = this.getValue();
15071         }
15072         
15073         if(this.disabled || this.allowBlank || v.length){
15074             this.markValid();
15075             return true;
15076         }
15077         
15078         this.markInvalid();
15079         return false;
15080     },
15081     
15082     tickableInputEl : function()
15083     {
15084         if(!this.tickable || !this.editable){
15085             return this.inputEl();
15086         }
15087         
15088         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15089     },
15090     
15091     
15092     getAutoCreateTouchView : function()
15093     {
15094         var id = Roo.id();
15095         
15096         var cfg = {
15097             cls: 'form-group' //input-group
15098         };
15099         
15100         var input =  {
15101             tag: 'input',
15102             id : id,
15103             type : this.inputType,
15104             cls : 'form-control x-combo-noedit',
15105             autocomplete: 'new-password',
15106             placeholder : this.placeholder || '',
15107             readonly : true
15108         };
15109         
15110         if (this.name) {
15111             input.name = this.name;
15112         }
15113         
15114         if (this.size) {
15115             input.cls += ' input-' + this.size;
15116         }
15117         
15118         if (this.disabled) {
15119             input.disabled = true;
15120         }
15121         
15122         var inputblock = {
15123             cls : '',
15124             cn : [
15125                 input
15126             ]
15127         };
15128         
15129         if(this.before){
15130             inputblock.cls += ' input-group';
15131             
15132             inputblock.cn.unshift({
15133                 tag :'span',
15134                 cls : 'input-group-addon input-group-prepend input-group-text',
15135                 html : this.before
15136             });
15137         }
15138         
15139         if(this.removable && !this.multiple){
15140             inputblock.cls += ' roo-removable';
15141             
15142             inputblock.cn.push({
15143                 tag: 'button',
15144                 html : 'x',
15145                 cls : 'roo-combo-removable-btn close'
15146             });
15147         }
15148
15149         if(this.hasFeedback && !this.allowBlank){
15150             
15151             inputblock.cls += ' has-feedback';
15152             
15153             inputblock.cn.push({
15154                 tag: 'span',
15155                 cls: 'glyphicon form-control-feedback'
15156             });
15157             
15158         }
15159         
15160         if (this.after) {
15161             
15162             inputblock.cls += (this.before) ? '' : ' input-group';
15163             
15164             inputblock.cn.push({
15165                 tag :'span',
15166                 cls : 'input-group-addon input-group-append input-group-text',
15167                 html : this.after
15168             });
15169         }
15170
15171         
15172         var ibwrap = inputblock;
15173         
15174         if(this.multiple){
15175             ibwrap = {
15176                 tag: 'ul',
15177                 cls: 'roo-select2-choices',
15178                 cn:[
15179                     {
15180                         tag: 'li',
15181                         cls: 'roo-select2-search-field',
15182                         cn: [
15183
15184                             inputblock
15185                         ]
15186                     }
15187                 ]
15188             };
15189         
15190             
15191         }
15192         
15193         var combobox = {
15194             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15195             cn: [
15196                 {
15197                     tag: 'input',
15198                     type : 'hidden',
15199                     cls: 'form-hidden-field'
15200                 },
15201                 ibwrap
15202             ]
15203         };
15204         
15205         if(!this.multiple && this.showToggleBtn){
15206             
15207             var caret = {
15208                         tag: 'span',
15209                         cls: 'caret'
15210             };
15211             
15212             if (this.caret != false) {
15213                 caret = {
15214                      tag: 'i',
15215                      cls: 'fa fa-' + this.caret
15216                 };
15217                 
15218             }
15219             
15220             combobox.cn.push({
15221                 tag :'span',
15222                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15223                 cn : [
15224                     caret,
15225                     {
15226                         tag: 'span',
15227                         cls: 'combobox-clear',
15228                         cn  : [
15229                             {
15230                                 tag : 'i',
15231                                 cls: 'icon-remove'
15232                             }
15233                         ]
15234                     }
15235                 ]
15236
15237             })
15238         }
15239         
15240         if(this.multiple){
15241             combobox.cls += ' roo-select2-container-multi';
15242         }
15243         
15244         var align = this.labelAlign || this.parentLabelAlign();
15245         
15246         if (align ==='left' && this.fieldLabel.length) {
15247
15248             cfg.cn = [
15249                 {
15250                    tag : 'i',
15251                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15252                    tooltip : 'This field is required'
15253                 },
15254                 {
15255                     tag: 'label',
15256                     cls : 'control-label col-form-label',
15257                     html : this.fieldLabel
15258
15259                 },
15260                 {
15261                     cls : '', 
15262                     cn: [
15263                         combobox
15264                     ]
15265                 }
15266             ];
15267             
15268             var labelCfg = cfg.cn[1];
15269             var contentCfg = cfg.cn[2];
15270             
15271
15272             if(this.indicatorpos == 'right'){
15273                 cfg.cn = [
15274                     {
15275                         tag: 'label',
15276                         'for' :  id,
15277                         cls : 'control-label col-form-label',
15278                         cn : [
15279                             {
15280                                 tag : 'span',
15281                                 html : this.fieldLabel
15282                             },
15283                             {
15284                                 tag : 'i',
15285                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15286                                 tooltip : 'This field is required'
15287                             }
15288                         ]
15289                     },
15290                     {
15291                         cls : "",
15292                         cn: [
15293                             combobox
15294                         ]
15295                     }
15296
15297                 ];
15298                 
15299                 labelCfg = cfg.cn[0];
15300                 contentCfg = cfg.cn[1];
15301             }
15302             
15303            
15304             
15305             if(this.labelWidth > 12){
15306                 labelCfg.style = "width: " + this.labelWidth + 'px';
15307             }
15308             
15309             if(this.labelWidth < 13 && this.labelmd == 0){
15310                 this.labelmd = this.labelWidth;
15311             }
15312             
15313             if(this.labellg > 0){
15314                 labelCfg.cls += ' col-lg-' + this.labellg;
15315                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15316             }
15317             
15318             if(this.labelmd > 0){
15319                 labelCfg.cls += ' col-md-' + this.labelmd;
15320                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15321             }
15322             
15323             if(this.labelsm > 0){
15324                 labelCfg.cls += ' col-sm-' + this.labelsm;
15325                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15326             }
15327             
15328             if(this.labelxs > 0){
15329                 labelCfg.cls += ' col-xs-' + this.labelxs;
15330                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15331             }
15332                 
15333                 
15334         } else if ( this.fieldLabel.length) {
15335             cfg.cn = [
15336                 {
15337                    tag : 'i',
15338                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15339                    tooltip : 'This field is required'
15340                 },
15341                 {
15342                     tag: 'label',
15343                     cls : 'control-label',
15344                     html : this.fieldLabel
15345
15346                 },
15347                 {
15348                     cls : '', 
15349                     cn: [
15350                         combobox
15351                     ]
15352                 }
15353             ];
15354             
15355             if(this.indicatorpos == 'right'){
15356                 cfg.cn = [
15357                     {
15358                         tag: 'label',
15359                         cls : 'control-label',
15360                         html : this.fieldLabel,
15361                         cn : [
15362                             {
15363                                tag : 'i',
15364                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15365                                tooltip : 'This field is required'
15366                             }
15367                         ]
15368                     },
15369                     {
15370                         cls : '', 
15371                         cn: [
15372                             combobox
15373                         ]
15374                     }
15375                 ];
15376             }
15377         } else {
15378             cfg.cn = combobox;    
15379         }
15380         
15381         
15382         var settings = this;
15383         
15384         ['xs','sm','md','lg'].map(function(size){
15385             if (settings[size]) {
15386                 cfg.cls += ' col-' + size + '-' + settings[size];
15387             }
15388         });
15389         
15390         return cfg;
15391     },
15392     
15393     initTouchView : function()
15394     {
15395         this.renderTouchView();
15396         
15397         this.touchViewEl.on('scroll', function(){
15398             this.el.dom.scrollTop = 0;
15399         }, this);
15400         
15401         this.originalValue = this.getValue();
15402         
15403         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15404         
15405         this.inputEl().on("click", this.showTouchView, this);
15406         if (this.triggerEl) {
15407             this.triggerEl.on("click", this.showTouchView, this);
15408         }
15409         
15410         
15411         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15412         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15413         
15414         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15415         
15416         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15417         this.store.on('load', this.onTouchViewLoad, this);
15418         this.store.on('loadexception', this.onTouchViewLoadException, this);
15419         
15420         if(this.hiddenName){
15421             
15422             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15423             
15424             this.hiddenField.dom.value =
15425                 this.hiddenValue !== undefined ? this.hiddenValue :
15426                 this.value !== undefined ? this.value : '';
15427         
15428             this.el.dom.removeAttribute('name');
15429             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15430         }
15431         
15432         if(this.multiple){
15433             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15434             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15435         }
15436         
15437         if(this.removable && !this.multiple){
15438             var close = this.closeTriggerEl();
15439             if(close){
15440                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15441                 close.on('click', this.removeBtnClick, this, close);
15442             }
15443         }
15444         /*
15445          * fix the bug in Safari iOS8
15446          */
15447         this.inputEl().on("focus", function(e){
15448             document.activeElement.blur();
15449         }, this);
15450         
15451         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15452         
15453         return;
15454         
15455         
15456     },
15457     
15458     renderTouchView : function()
15459     {
15460         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15461         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15462         
15463         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15464         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15465         
15466         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15467         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15468         this.touchViewBodyEl.setStyle('overflow', 'auto');
15469         
15470         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15471         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15472         
15473         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15474         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15475         
15476     },
15477     
15478     showTouchView : function()
15479     {
15480         if(this.disabled){
15481             return;
15482         }
15483         
15484         this.touchViewHeaderEl.hide();
15485
15486         if(this.modalTitle.length){
15487             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15488             this.touchViewHeaderEl.show();
15489         }
15490
15491         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15492         this.touchViewEl.show();
15493
15494         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15495         
15496         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15497         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15498
15499         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15500
15501         if(this.modalTitle.length){
15502             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15503         }
15504         
15505         this.touchViewBodyEl.setHeight(bodyHeight);
15506
15507         if(this.animate){
15508             var _this = this;
15509             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15510         }else{
15511             this.touchViewEl.addClass('in');
15512         }
15513         
15514         if(this._touchViewMask){
15515             Roo.get(document.body).addClass("x-body-masked");
15516             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15517             this._touchViewMask.setStyle('z-index', 10000);
15518             this._touchViewMask.addClass('show');
15519         }
15520         
15521         this.doTouchViewQuery();
15522         
15523     },
15524     
15525     hideTouchView : function()
15526     {
15527         this.touchViewEl.removeClass('in');
15528
15529         if(this.animate){
15530             var _this = this;
15531             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15532         }else{
15533             this.touchViewEl.setStyle('display', 'none');
15534         }
15535         
15536         if(this._touchViewMask){
15537             this._touchViewMask.removeClass('show');
15538             Roo.get(document.body).removeClass("x-body-masked");
15539         }
15540     },
15541     
15542     setTouchViewValue : function()
15543     {
15544         if(this.multiple){
15545             this.clearItem();
15546         
15547             var _this = this;
15548
15549             Roo.each(this.tickItems, function(o){
15550                 this.addItem(o);
15551             }, this);
15552         }
15553         
15554         this.hideTouchView();
15555     },
15556     
15557     doTouchViewQuery : function()
15558     {
15559         var qe = {
15560             query: '',
15561             forceAll: true,
15562             combo: this,
15563             cancel:false
15564         };
15565         
15566         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15567             return false;
15568         }
15569         
15570         if(!this.alwaysQuery || this.mode == 'local'){
15571             this.onTouchViewLoad();
15572             return;
15573         }
15574         
15575         this.store.load();
15576     },
15577     
15578     onTouchViewBeforeLoad : function(combo,opts)
15579     {
15580         return;
15581     },
15582
15583     // private
15584     onTouchViewLoad : function()
15585     {
15586         if(this.store.getCount() < 1){
15587             this.onTouchViewEmptyResults();
15588             return;
15589         }
15590         
15591         this.clearTouchView();
15592         
15593         var rawValue = this.getRawValue();
15594         
15595         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15596         
15597         this.tickItems = [];
15598         
15599         this.store.data.each(function(d, rowIndex){
15600             var row = this.touchViewListGroup.createChild(template);
15601             
15602             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15603                 row.addClass(d.data.cls);
15604             }
15605             
15606             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15607                 var cfg = {
15608                     data : d.data,
15609                     html : d.data[this.displayField]
15610                 };
15611                 
15612                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15613                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15614                 }
15615             }
15616             row.removeClass('selected');
15617             if(!this.multiple && this.valueField &&
15618                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15619             {
15620                 // radio buttons..
15621                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15622                 row.addClass('selected');
15623             }
15624             
15625             if(this.multiple && this.valueField &&
15626                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15627             {
15628                 
15629                 // checkboxes...
15630                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15631                 this.tickItems.push(d.data);
15632             }
15633             
15634             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15635             
15636         }, this);
15637         
15638         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15639         
15640         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15641
15642         if(this.modalTitle.length){
15643             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15644         }
15645
15646         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15647         
15648         if(this.mobile_restrict_height && listHeight < bodyHeight){
15649             this.touchViewBodyEl.setHeight(listHeight);
15650         }
15651         
15652         var _this = this;
15653         
15654         if(firstChecked && listHeight > bodyHeight){
15655             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15656         }
15657         
15658     },
15659     
15660     onTouchViewLoadException : function()
15661     {
15662         this.hideTouchView();
15663     },
15664     
15665     onTouchViewEmptyResults : function()
15666     {
15667         this.clearTouchView();
15668         
15669         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15670         
15671         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15672         
15673     },
15674     
15675     clearTouchView : function()
15676     {
15677         this.touchViewListGroup.dom.innerHTML = '';
15678     },
15679     
15680     onTouchViewClick : function(e, el, o)
15681     {
15682         e.preventDefault();
15683         
15684         var row = o.row;
15685         var rowIndex = o.rowIndex;
15686         
15687         var r = this.store.getAt(rowIndex);
15688         
15689         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15690             
15691             if(!this.multiple){
15692                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15693                     c.dom.removeAttribute('checked');
15694                 }, this);
15695
15696                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15697
15698                 this.setFromData(r.data);
15699
15700                 var close = this.closeTriggerEl();
15701
15702                 if(close){
15703                     close.show();
15704                 }
15705
15706                 this.hideTouchView();
15707
15708                 this.fireEvent('select', this, r, rowIndex);
15709
15710                 return;
15711             }
15712
15713             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15714                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15715                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15716                 return;
15717             }
15718
15719             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15720             this.addItem(r.data);
15721             this.tickItems.push(r.data);
15722         }
15723     },
15724     
15725     getAutoCreateNativeIOS : function()
15726     {
15727         var cfg = {
15728             cls: 'form-group' //input-group,
15729         };
15730         
15731         var combobox =  {
15732             tag: 'select',
15733             cls : 'roo-ios-select'
15734         };
15735         
15736         if (this.name) {
15737             combobox.name = this.name;
15738         }
15739         
15740         if (this.disabled) {
15741             combobox.disabled = true;
15742         }
15743         
15744         var settings = this;
15745         
15746         ['xs','sm','md','lg'].map(function(size){
15747             if (settings[size]) {
15748                 cfg.cls += ' col-' + size + '-' + settings[size];
15749             }
15750         });
15751         
15752         cfg.cn = combobox;
15753         
15754         return cfg;
15755         
15756     },
15757     
15758     initIOSView : function()
15759     {
15760         this.store.on('load', this.onIOSViewLoad, this);
15761         
15762         return;
15763     },
15764     
15765     onIOSViewLoad : function()
15766     {
15767         if(this.store.getCount() < 1){
15768             return;
15769         }
15770         
15771         this.clearIOSView();
15772         
15773         if(this.allowBlank) {
15774             
15775             var default_text = '-- SELECT --';
15776             
15777             if(this.placeholder.length){
15778                 default_text = this.placeholder;
15779             }
15780             
15781             if(this.emptyTitle.length){
15782                 default_text += ' - ' + this.emptyTitle + ' -';
15783             }
15784             
15785             var opt = this.inputEl().createChild({
15786                 tag: 'option',
15787                 value : 0,
15788                 html : default_text
15789             });
15790             
15791             var o = {};
15792             o[this.valueField] = 0;
15793             o[this.displayField] = default_text;
15794             
15795             this.ios_options.push({
15796                 data : o,
15797                 el : opt
15798             });
15799             
15800         }
15801         
15802         this.store.data.each(function(d, rowIndex){
15803             
15804             var html = '';
15805             
15806             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15807                 html = d.data[this.displayField];
15808             }
15809             
15810             var value = '';
15811             
15812             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15813                 value = d.data[this.valueField];
15814             }
15815             
15816             var option = {
15817                 tag: 'option',
15818                 value : value,
15819                 html : html
15820             };
15821             
15822             if(this.value == d.data[this.valueField]){
15823                 option['selected'] = true;
15824             }
15825             
15826             var opt = this.inputEl().createChild(option);
15827             
15828             this.ios_options.push({
15829                 data : d.data,
15830                 el : opt
15831             });
15832             
15833         }, this);
15834         
15835         this.inputEl().on('change', function(){
15836            this.fireEvent('select', this);
15837         }, this);
15838         
15839     },
15840     
15841     clearIOSView: function()
15842     {
15843         this.inputEl().dom.innerHTML = '';
15844         
15845         this.ios_options = [];
15846     },
15847     
15848     setIOSValue: function(v)
15849     {
15850         this.value = v;
15851         
15852         if(!this.ios_options){
15853             return;
15854         }
15855         
15856         Roo.each(this.ios_options, function(opts){
15857            
15858            opts.el.dom.removeAttribute('selected');
15859            
15860            if(opts.data[this.valueField] != v){
15861                return;
15862            }
15863            
15864            opts.el.dom.setAttribute('selected', true);
15865            
15866         }, this);
15867     }
15868
15869     /** 
15870     * @cfg {Boolean} grow 
15871     * @hide 
15872     */
15873     /** 
15874     * @cfg {Number} growMin 
15875     * @hide 
15876     */
15877     /** 
15878     * @cfg {Number} growMax 
15879     * @hide 
15880     */
15881     /**
15882      * @hide
15883      * @method autoSize
15884      */
15885 });
15886
15887 Roo.apply(Roo.bootstrap.ComboBox,  {
15888     
15889     header : {
15890         tag: 'div',
15891         cls: 'modal-header',
15892         cn: [
15893             {
15894                 tag: 'h4',
15895                 cls: 'modal-title'
15896             }
15897         ]
15898     },
15899     
15900     body : {
15901         tag: 'div',
15902         cls: 'modal-body',
15903         cn: [
15904             {
15905                 tag: 'ul',
15906                 cls: 'list-group'
15907             }
15908         ]
15909     },
15910     
15911     listItemRadio : {
15912         tag: 'li',
15913         cls: 'list-group-item',
15914         cn: [
15915             {
15916                 tag: 'span',
15917                 cls: 'roo-combobox-list-group-item-value'
15918             },
15919             {
15920                 tag: 'div',
15921                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15922                 cn: [
15923                     {
15924                         tag: 'input',
15925                         type: 'radio'
15926                     },
15927                     {
15928                         tag: 'label'
15929                     }
15930                 ]
15931             }
15932         ]
15933     },
15934     
15935     listItemCheckbox : {
15936         tag: 'li',
15937         cls: 'list-group-item',
15938         cn: [
15939             {
15940                 tag: 'span',
15941                 cls: 'roo-combobox-list-group-item-value'
15942             },
15943             {
15944                 tag: 'div',
15945                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15946                 cn: [
15947                     {
15948                         tag: 'input',
15949                         type: 'checkbox'
15950                     },
15951                     {
15952                         tag: 'label'
15953                     }
15954                 ]
15955             }
15956         ]
15957     },
15958     
15959     emptyResult : {
15960         tag: 'div',
15961         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15962     },
15963     
15964     footer : {
15965         tag: 'div',
15966         cls: 'modal-footer',
15967         cn: [
15968             {
15969                 tag: 'div',
15970                 cls: 'row',
15971                 cn: [
15972                     {
15973                         tag: 'div',
15974                         cls: 'col-xs-6 text-left',
15975                         cn: {
15976                             tag: 'button',
15977                             cls: 'btn btn-danger roo-touch-view-cancel',
15978                             html: 'Cancel'
15979                         }
15980                     },
15981                     {
15982                         tag: 'div',
15983                         cls: 'col-xs-6 text-right',
15984                         cn: {
15985                             tag: 'button',
15986                             cls: 'btn btn-success roo-touch-view-ok',
15987                             html: 'OK'
15988                         }
15989                     }
15990                 ]
15991             }
15992         ]
15993         
15994     }
15995 });
15996
15997 Roo.apply(Roo.bootstrap.ComboBox,  {
15998     
15999     touchViewTemplate : {
16000         tag: 'div',
16001         cls: 'modal fade roo-combobox-touch-view',
16002         cn: [
16003             {
16004                 tag: 'div',
16005                 cls: 'modal-dialog',
16006                 style : 'position:fixed', // we have to fix position....
16007                 cn: [
16008                     {
16009                         tag: 'div',
16010                         cls: 'modal-content',
16011                         cn: [
16012                             Roo.bootstrap.ComboBox.header,
16013                             Roo.bootstrap.ComboBox.body,
16014                             Roo.bootstrap.ComboBox.footer
16015                         ]
16016                     }
16017                 ]
16018             }
16019         ]
16020     }
16021 });/*
16022  * Based on:
16023  * Ext JS Library 1.1.1
16024  * Copyright(c) 2006-2007, Ext JS, LLC.
16025  *
16026  * Originally Released Under LGPL - original licence link has changed is not relivant.
16027  *
16028  * Fork - LGPL
16029  * <script type="text/javascript">
16030  */
16031
16032 /**
16033  * @class Roo.View
16034  * @extends Roo.util.Observable
16035  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16036  * This class also supports single and multi selection modes. <br>
16037  * Create a data model bound view:
16038  <pre><code>
16039  var store = new Roo.data.Store(...);
16040
16041  var view = new Roo.View({
16042     el : "my-element",
16043     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16044  
16045     singleSelect: true,
16046     selectedClass: "ydataview-selected",
16047     store: store
16048  });
16049
16050  // listen for node click?
16051  view.on("click", function(vw, index, node, e){
16052  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16053  });
16054
16055  // load XML data
16056  dataModel.load("foobar.xml");
16057  </code></pre>
16058  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16059  * <br><br>
16060  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16061  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16062  * 
16063  * Note: old style constructor is still suported (container, template, config)
16064  * 
16065  * @constructor
16066  * Create a new View
16067  * @param {Object} config The config object
16068  * 
16069  */
16070 Roo.View = function(config, depreciated_tpl, depreciated_config){
16071     
16072     this.parent = false;
16073     
16074     if (typeof(depreciated_tpl) == 'undefined') {
16075         // new way.. - universal constructor.
16076         Roo.apply(this, config);
16077         this.el  = Roo.get(this.el);
16078     } else {
16079         // old format..
16080         this.el  = Roo.get(config);
16081         this.tpl = depreciated_tpl;
16082         Roo.apply(this, depreciated_config);
16083     }
16084     this.wrapEl  = this.el.wrap().wrap();
16085     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16086     
16087     
16088     if(typeof(this.tpl) == "string"){
16089         this.tpl = new Roo.Template(this.tpl);
16090     } else {
16091         // support xtype ctors..
16092         this.tpl = new Roo.factory(this.tpl, Roo);
16093     }
16094     
16095     
16096     this.tpl.compile();
16097     
16098     /** @private */
16099     this.addEvents({
16100         /**
16101          * @event beforeclick
16102          * Fires before a click is processed. Returns false to cancel the default action.
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             "beforeclick" : true,
16109         /**
16110          * @event click
16111          * Fires when a template node is 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             "click" : true,
16118         /**
16119          * @event dblclick
16120          * Fires when a template node is double 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             "dblclick" : true,
16127         /**
16128          * @event contextmenu
16129          * Fires when a template node is right clicked.
16130          * @param {Roo.View} this
16131          * @param {Number} index The index of the target node
16132          * @param {HTMLElement} node The target node
16133          * @param {Roo.EventObject} e The raw event object
16134          */
16135             "contextmenu" : true,
16136         /**
16137          * @event selectionchange
16138          * Fires when the selected nodes change.
16139          * @param {Roo.View} this
16140          * @param {Array} selections Array of the selected nodes
16141          */
16142             "selectionchange" : true,
16143     
16144         /**
16145          * @event beforeselect
16146          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16147          * @param {Roo.View} this
16148          * @param {HTMLElement} node The node to be selected
16149          * @param {Array} selections Array of currently selected nodes
16150          */
16151             "beforeselect" : true,
16152         /**
16153          * @event preparedata
16154          * Fires on every row to render, to allow you to change the data.
16155          * @param {Roo.View} this
16156          * @param {Object} data to be rendered (change this)
16157          */
16158           "preparedata" : true
16159           
16160           
16161         });
16162
16163
16164
16165     this.el.on({
16166         "click": this.onClick,
16167         "dblclick": this.onDblClick,
16168         "contextmenu": this.onContextMenu,
16169         scope:this
16170     });
16171
16172     this.selections = [];
16173     this.nodes = [];
16174     this.cmp = new Roo.CompositeElementLite([]);
16175     if(this.store){
16176         this.store = Roo.factory(this.store, Roo.data);
16177         this.setStore(this.store, true);
16178     }
16179     
16180     if ( this.footer && this.footer.xtype) {
16181            
16182          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16183         
16184         this.footer.dataSource = this.store;
16185         this.footer.container = fctr;
16186         this.footer = Roo.factory(this.footer, Roo);
16187         fctr.insertFirst(this.el);
16188         
16189         // this is a bit insane - as the paging toolbar seems to detach the el..
16190 //        dom.parentNode.parentNode.parentNode
16191          // they get detached?
16192     }
16193     
16194     
16195     Roo.View.superclass.constructor.call(this);
16196     
16197     
16198 };
16199
16200 Roo.extend(Roo.View, Roo.util.Observable, {
16201     
16202      /**
16203      * @cfg {Roo.data.Store} store Data store to load data from.
16204      */
16205     store : false,
16206     
16207     /**
16208      * @cfg {String|Roo.Element} el The container element.
16209      */
16210     el : '',
16211     
16212     /**
16213      * @cfg {String|Roo.Template} tpl The template used by this View 
16214      */
16215     tpl : false,
16216     /**
16217      * @cfg {String} dataName the named area of the template to use as the data area
16218      *                          Works with domtemplates roo-name="name"
16219      */
16220     dataName: false,
16221     /**
16222      * @cfg {String} selectedClass The css class to add to selected nodes
16223      */
16224     selectedClass : "x-view-selected",
16225      /**
16226      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16227      */
16228     emptyText : "",
16229     
16230     /**
16231      * @cfg {String} text to display on mask (default Loading)
16232      */
16233     mask : false,
16234     /**
16235      * @cfg {Boolean} multiSelect Allow multiple selection
16236      */
16237     multiSelect : false,
16238     /**
16239      * @cfg {Boolean} singleSelect Allow single selection
16240      */
16241     singleSelect:  false,
16242     
16243     /**
16244      * @cfg {Boolean} toggleSelect - selecting 
16245      */
16246     toggleSelect : false,
16247     
16248     /**
16249      * @cfg {Boolean} tickable - selecting 
16250      */
16251     tickable : false,
16252     
16253     /**
16254      * Returns the element this view is bound to.
16255      * @return {Roo.Element}
16256      */
16257     getEl : function(){
16258         return this.wrapEl;
16259     },
16260     
16261     
16262
16263     /**
16264      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16265      */
16266     refresh : function(){
16267         //Roo.log('refresh');
16268         var t = this.tpl;
16269         
16270         // if we are using something like 'domtemplate', then
16271         // the what gets used is:
16272         // t.applySubtemplate(NAME, data, wrapping data..)
16273         // the outer template then get' applied with
16274         //     the store 'extra data'
16275         // and the body get's added to the
16276         //      roo-name="data" node?
16277         //      <span class='roo-tpl-{name}'></span> ?????
16278         
16279         
16280         
16281         this.clearSelections();
16282         this.el.update("");
16283         var html = [];
16284         var records = this.store.getRange();
16285         if(records.length < 1) {
16286             
16287             // is this valid??  = should it render a template??
16288             
16289             this.el.update(this.emptyText);
16290             return;
16291         }
16292         var el = this.el;
16293         if (this.dataName) {
16294             this.el.update(t.apply(this.store.meta)); //????
16295             el = this.el.child('.roo-tpl-' + this.dataName);
16296         }
16297         
16298         for(var i = 0, len = records.length; i < len; i++){
16299             var data = this.prepareData(records[i].data, i, records[i]);
16300             this.fireEvent("preparedata", this, data, i, records[i]);
16301             
16302             var d = Roo.apply({}, data);
16303             
16304             if(this.tickable){
16305                 Roo.apply(d, {'roo-id' : Roo.id()});
16306                 
16307                 var _this = this;
16308             
16309                 Roo.each(this.parent.item, function(item){
16310                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16311                         return;
16312                     }
16313                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16314                 });
16315             }
16316             
16317             html[html.length] = Roo.util.Format.trim(
16318                 this.dataName ?
16319                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16320                     t.apply(d)
16321             );
16322         }
16323         
16324         
16325         
16326         el.update(html.join(""));
16327         this.nodes = el.dom.childNodes;
16328         this.updateIndexes(0);
16329     },
16330     
16331
16332     /**
16333      * Function to override to reformat the data that is sent to
16334      * the template for each node.
16335      * DEPRICATED - use the preparedata event handler.
16336      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16337      * a JSON object for an UpdateManager bound view).
16338      */
16339     prepareData : function(data, index, record)
16340     {
16341         this.fireEvent("preparedata", this, data, index, record);
16342         return data;
16343     },
16344
16345     onUpdate : function(ds, record){
16346         // Roo.log('on update');   
16347         this.clearSelections();
16348         var index = this.store.indexOf(record);
16349         var n = this.nodes[index];
16350         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16351         n.parentNode.removeChild(n);
16352         this.updateIndexes(index, index);
16353     },
16354
16355     
16356     
16357 // --------- FIXME     
16358     onAdd : function(ds, records, index)
16359     {
16360         //Roo.log(['on Add', ds, records, index] );        
16361         this.clearSelections();
16362         if(this.nodes.length == 0){
16363             this.refresh();
16364             return;
16365         }
16366         var n = this.nodes[index];
16367         for(var i = 0, len = records.length; i < len; i++){
16368             var d = this.prepareData(records[i].data, i, records[i]);
16369             if(n){
16370                 this.tpl.insertBefore(n, d);
16371             }else{
16372                 
16373                 this.tpl.append(this.el, d);
16374             }
16375         }
16376         this.updateIndexes(index);
16377     },
16378
16379     onRemove : function(ds, record, index){
16380        // Roo.log('onRemove');
16381         this.clearSelections();
16382         var el = this.dataName  ?
16383             this.el.child('.roo-tpl-' + this.dataName) :
16384             this.el; 
16385         
16386         el.dom.removeChild(this.nodes[index]);
16387         this.updateIndexes(index);
16388     },
16389
16390     /**
16391      * Refresh an individual node.
16392      * @param {Number} index
16393      */
16394     refreshNode : function(index){
16395         this.onUpdate(this.store, this.store.getAt(index));
16396     },
16397
16398     updateIndexes : function(startIndex, endIndex){
16399         var ns = this.nodes;
16400         startIndex = startIndex || 0;
16401         endIndex = endIndex || ns.length - 1;
16402         for(var i = startIndex; i <= endIndex; i++){
16403             ns[i].nodeIndex = i;
16404         }
16405     },
16406
16407     /**
16408      * Changes the data store this view uses and refresh the view.
16409      * @param {Store} store
16410      */
16411     setStore : function(store, initial){
16412         if(!initial && this.store){
16413             this.store.un("datachanged", this.refresh);
16414             this.store.un("add", this.onAdd);
16415             this.store.un("remove", this.onRemove);
16416             this.store.un("update", this.onUpdate);
16417             this.store.un("clear", this.refresh);
16418             this.store.un("beforeload", this.onBeforeLoad);
16419             this.store.un("load", this.onLoad);
16420             this.store.un("loadexception", this.onLoad);
16421         }
16422         if(store){
16423           
16424             store.on("datachanged", this.refresh, this);
16425             store.on("add", this.onAdd, this);
16426             store.on("remove", this.onRemove, this);
16427             store.on("update", this.onUpdate, this);
16428             store.on("clear", this.refresh, this);
16429             store.on("beforeload", this.onBeforeLoad, this);
16430             store.on("load", this.onLoad, this);
16431             store.on("loadexception", this.onLoad, this);
16432         }
16433         
16434         if(store){
16435             this.refresh();
16436         }
16437     },
16438     /**
16439      * onbeforeLoad - masks the loading area.
16440      *
16441      */
16442     onBeforeLoad : function(store,opts)
16443     {
16444          //Roo.log('onBeforeLoad');   
16445         if (!opts.add) {
16446             this.el.update("");
16447         }
16448         this.el.mask(this.mask ? this.mask : "Loading" ); 
16449     },
16450     onLoad : function ()
16451     {
16452         this.el.unmask();
16453     },
16454     
16455
16456     /**
16457      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16458      * @param {HTMLElement} node
16459      * @return {HTMLElement} The template node
16460      */
16461     findItemFromChild : function(node){
16462         var el = this.dataName  ?
16463             this.el.child('.roo-tpl-' + this.dataName,true) :
16464             this.el.dom; 
16465         
16466         if(!node || node.parentNode == el){
16467                     return node;
16468             }
16469             var p = node.parentNode;
16470             while(p && p != el){
16471             if(p.parentNode == el){
16472                 return p;
16473             }
16474             p = p.parentNode;
16475         }
16476             return null;
16477     },
16478
16479     /** @ignore */
16480     onClick : function(e){
16481         var item = this.findItemFromChild(e.getTarget());
16482         if(item){
16483             var index = this.indexOf(item);
16484             if(this.onItemClick(item, index, e) !== false){
16485                 this.fireEvent("click", this, index, item, e);
16486             }
16487         }else{
16488             this.clearSelections();
16489         }
16490     },
16491
16492     /** @ignore */
16493     onContextMenu : function(e){
16494         var item = this.findItemFromChild(e.getTarget());
16495         if(item){
16496             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16497         }
16498     },
16499
16500     /** @ignore */
16501     onDblClick : function(e){
16502         var item = this.findItemFromChild(e.getTarget());
16503         if(item){
16504             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16505         }
16506     },
16507
16508     onItemClick : function(item, index, e)
16509     {
16510         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16511             return false;
16512         }
16513         if (this.toggleSelect) {
16514             var m = this.isSelected(item) ? 'unselect' : 'select';
16515             //Roo.log(m);
16516             var _t = this;
16517             _t[m](item, true, false);
16518             return true;
16519         }
16520         if(this.multiSelect || this.singleSelect){
16521             if(this.multiSelect && e.shiftKey && this.lastSelection){
16522                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16523             }else{
16524                 this.select(item, this.multiSelect && e.ctrlKey);
16525                 this.lastSelection = item;
16526             }
16527             
16528             if(!this.tickable){
16529                 e.preventDefault();
16530             }
16531             
16532         }
16533         return true;
16534     },
16535
16536     /**
16537      * Get the number of selected nodes.
16538      * @return {Number}
16539      */
16540     getSelectionCount : function(){
16541         return this.selections.length;
16542     },
16543
16544     /**
16545      * Get the currently selected nodes.
16546      * @return {Array} An array of HTMLElements
16547      */
16548     getSelectedNodes : function(){
16549         return this.selections;
16550     },
16551
16552     /**
16553      * Get the indexes of the selected nodes.
16554      * @return {Array}
16555      */
16556     getSelectedIndexes : function(){
16557         var indexes = [], s = this.selections;
16558         for(var i = 0, len = s.length; i < len; i++){
16559             indexes.push(s[i].nodeIndex);
16560         }
16561         return indexes;
16562     },
16563
16564     /**
16565      * Clear all selections
16566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16567      */
16568     clearSelections : function(suppressEvent){
16569         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16570             this.cmp.elements = this.selections;
16571             this.cmp.removeClass(this.selectedClass);
16572             this.selections = [];
16573             if(!suppressEvent){
16574                 this.fireEvent("selectionchange", this, this.selections);
16575             }
16576         }
16577     },
16578
16579     /**
16580      * Returns true if the passed node is selected
16581      * @param {HTMLElement/Number} node The node or node index
16582      * @return {Boolean}
16583      */
16584     isSelected : function(node){
16585         var s = this.selections;
16586         if(s.length < 1){
16587             return false;
16588         }
16589         node = this.getNode(node);
16590         return s.indexOf(node) !== -1;
16591     },
16592
16593     /**
16594      * Selects nodes.
16595      * @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
16596      * @param {Boolean} keepExisting (optional) true to keep existing selections
16597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16598      */
16599     select : function(nodeInfo, keepExisting, suppressEvent){
16600         if(nodeInfo instanceof Array){
16601             if(!keepExisting){
16602                 this.clearSelections(true);
16603             }
16604             for(var i = 0, len = nodeInfo.length; i < len; i++){
16605                 this.select(nodeInfo[i], true, true);
16606             }
16607             return;
16608         } 
16609         var node = this.getNode(nodeInfo);
16610         if(!node || this.isSelected(node)){
16611             return; // already selected.
16612         }
16613         if(!keepExisting){
16614             this.clearSelections(true);
16615         }
16616         
16617         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16618             Roo.fly(node).addClass(this.selectedClass);
16619             this.selections.push(node);
16620             if(!suppressEvent){
16621                 this.fireEvent("selectionchange", this, this.selections);
16622             }
16623         }
16624         
16625         
16626     },
16627       /**
16628      * Unselects nodes.
16629      * @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
16630      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16631      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16632      */
16633     unselect : function(nodeInfo, keepExisting, suppressEvent)
16634     {
16635         if(nodeInfo instanceof Array){
16636             Roo.each(this.selections, function(s) {
16637                 this.unselect(s, nodeInfo);
16638             }, this);
16639             return;
16640         }
16641         var node = this.getNode(nodeInfo);
16642         if(!node || !this.isSelected(node)){
16643             //Roo.log("not selected");
16644             return; // not selected.
16645         }
16646         // fireevent???
16647         var ns = [];
16648         Roo.each(this.selections, function(s) {
16649             if (s == node ) {
16650                 Roo.fly(node).removeClass(this.selectedClass);
16651
16652                 return;
16653             }
16654             ns.push(s);
16655         },this);
16656         
16657         this.selections= ns;
16658         this.fireEvent("selectionchange", this, this.selections);
16659     },
16660
16661     /**
16662      * Gets a template node.
16663      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16664      * @return {HTMLElement} The node or null if it wasn't found
16665      */
16666     getNode : function(nodeInfo){
16667         if(typeof nodeInfo == "string"){
16668             return document.getElementById(nodeInfo);
16669         }else if(typeof nodeInfo == "number"){
16670             return this.nodes[nodeInfo];
16671         }
16672         return nodeInfo;
16673     },
16674
16675     /**
16676      * Gets a range template nodes.
16677      * @param {Number} startIndex
16678      * @param {Number} endIndex
16679      * @return {Array} An array of nodes
16680      */
16681     getNodes : function(start, end){
16682         var ns = this.nodes;
16683         start = start || 0;
16684         end = typeof end == "undefined" ? ns.length - 1 : end;
16685         var nodes = [];
16686         if(start <= end){
16687             for(var i = start; i <= end; i++){
16688                 nodes.push(ns[i]);
16689             }
16690         } else{
16691             for(var i = start; i >= end; i--){
16692                 nodes.push(ns[i]);
16693             }
16694         }
16695         return nodes;
16696     },
16697
16698     /**
16699      * Finds the index of the passed node
16700      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16701      * @return {Number} The index of the node or -1
16702      */
16703     indexOf : function(node){
16704         node = this.getNode(node);
16705         if(typeof node.nodeIndex == "number"){
16706             return node.nodeIndex;
16707         }
16708         var ns = this.nodes;
16709         for(var i = 0, len = ns.length; i < len; i++){
16710             if(ns[i] == node){
16711                 return i;
16712             }
16713         }
16714         return -1;
16715     }
16716 });
16717 /*
16718  * - LGPL
16719  *
16720  * based on jquery fullcalendar
16721  * 
16722  */
16723
16724 Roo.bootstrap = Roo.bootstrap || {};
16725 /**
16726  * @class Roo.bootstrap.Calendar
16727  * @extends Roo.bootstrap.Component
16728  * Bootstrap Calendar class
16729  * @cfg {Boolean} loadMask (true|false) default false
16730  * @cfg {Object} header generate the user specific header of the calendar, default false
16731
16732  * @constructor
16733  * Create a new Container
16734  * @param {Object} config The config object
16735  */
16736
16737
16738
16739 Roo.bootstrap.Calendar = function(config){
16740     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16741      this.addEvents({
16742         /**
16743              * @event select
16744              * Fires when a date is selected
16745              * @param {DatePicker} this
16746              * @param {Date} date The selected date
16747              */
16748         'select': true,
16749         /**
16750              * @event monthchange
16751              * Fires when the displayed month changes 
16752              * @param {DatePicker} this
16753              * @param {Date} date The selected month
16754              */
16755         'monthchange': true,
16756         /**
16757              * @event evententer
16758              * Fires when mouse over an event
16759              * @param {Calendar} this
16760              * @param {event} Event
16761              */
16762         'evententer': true,
16763         /**
16764              * @event eventleave
16765              * Fires when the mouse leaves an
16766              * @param {Calendar} this
16767              * @param {event}
16768              */
16769         'eventleave': true,
16770         /**
16771              * @event eventclick
16772              * Fires when the mouse click an
16773              * @param {Calendar} this
16774              * @param {event}
16775              */
16776         'eventclick': true
16777         
16778     });
16779
16780 };
16781
16782 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16783     
16784      /**
16785      * @cfg {Number} startDay
16786      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16787      */
16788     startDay : 0,
16789     
16790     loadMask : false,
16791     
16792     header : false,
16793       
16794     getAutoCreate : function(){
16795         
16796         
16797         var fc_button = function(name, corner, style, content ) {
16798             return Roo.apply({},{
16799                 tag : 'span',
16800                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16801                          (corner.length ?
16802                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16803                             ''
16804                         ),
16805                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16806                 unselectable: 'on'
16807             });
16808         };
16809         
16810         var header = {};
16811         
16812         if(!this.header){
16813             header = {
16814                 tag : 'table',
16815                 cls : 'fc-header',
16816                 style : 'width:100%',
16817                 cn : [
16818                     {
16819                         tag: 'tr',
16820                         cn : [
16821                             {
16822                                 tag : 'td',
16823                                 cls : 'fc-header-left',
16824                                 cn : [
16825                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16826                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16827                                     { tag: 'span', cls: 'fc-header-space' },
16828                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16829
16830
16831                                 ]
16832                             },
16833
16834                             {
16835                                 tag : 'td',
16836                                 cls : 'fc-header-center',
16837                                 cn : [
16838                                     {
16839                                         tag: 'span',
16840                                         cls: 'fc-header-title',
16841                                         cn : {
16842                                             tag: 'H2',
16843                                             html : 'month / year'
16844                                         }
16845                                     }
16846
16847                                 ]
16848                             },
16849                             {
16850                                 tag : 'td',
16851                                 cls : 'fc-header-right',
16852                                 cn : [
16853                               /*      fc_button('month', 'left', '', 'month' ),
16854                                     fc_button('week', '', '', 'week' ),
16855                                     fc_button('day', 'right', '', 'day' )
16856                                 */    
16857
16858                                 ]
16859                             }
16860
16861                         ]
16862                     }
16863                 ]
16864             };
16865         }
16866         
16867         header = this.header;
16868         
16869        
16870         var cal_heads = function() {
16871             var ret = [];
16872             // fixme - handle this.
16873             
16874             for (var i =0; i < Date.dayNames.length; i++) {
16875                 var d = Date.dayNames[i];
16876                 ret.push({
16877                     tag: 'th',
16878                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16879                     html : d.substring(0,3)
16880                 });
16881                 
16882             }
16883             ret[0].cls += ' fc-first';
16884             ret[6].cls += ' fc-last';
16885             return ret;
16886         };
16887         var cal_cell = function(n) {
16888             return  {
16889                 tag: 'td',
16890                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16891                 cn : [
16892                     {
16893                         cn : [
16894                             {
16895                                 cls: 'fc-day-number',
16896                                 html: 'D'
16897                             },
16898                             {
16899                                 cls: 'fc-day-content',
16900                              
16901                                 cn : [
16902                                      {
16903                                         style: 'position: relative;' // height: 17px;
16904                                     }
16905                                 ]
16906                             }
16907                             
16908                             
16909                         ]
16910                     }
16911                 ]
16912                 
16913             }
16914         };
16915         var cal_rows = function() {
16916             
16917             var ret = [];
16918             for (var r = 0; r < 6; r++) {
16919                 var row= {
16920                     tag : 'tr',
16921                     cls : 'fc-week',
16922                     cn : []
16923                 };
16924                 
16925                 for (var i =0; i < Date.dayNames.length; i++) {
16926                     var d = Date.dayNames[i];
16927                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16928
16929                 }
16930                 row.cn[0].cls+=' fc-first';
16931                 row.cn[0].cn[0].style = 'min-height:90px';
16932                 row.cn[6].cls+=' fc-last';
16933                 ret.push(row);
16934                 
16935             }
16936             ret[0].cls += ' fc-first';
16937             ret[4].cls += ' fc-prev-last';
16938             ret[5].cls += ' fc-last';
16939             return ret;
16940             
16941         };
16942         
16943         var cal_table = {
16944             tag: 'table',
16945             cls: 'fc-border-separate',
16946             style : 'width:100%',
16947             cellspacing  : 0,
16948             cn : [
16949                 { 
16950                     tag: 'thead',
16951                     cn : [
16952                         { 
16953                             tag: 'tr',
16954                             cls : 'fc-first fc-last',
16955                             cn : cal_heads()
16956                         }
16957                     ]
16958                 },
16959                 { 
16960                     tag: 'tbody',
16961                     cn : cal_rows()
16962                 }
16963                   
16964             ]
16965         };
16966          
16967          var cfg = {
16968             cls : 'fc fc-ltr',
16969             cn : [
16970                 header,
16971                 {
16972                     cls : 'fc-content',
16973                     style : "position: relative;",
16974                     cn : [
16975                         {
16976                             cls : 'fc-view fc-view-month fc-grid',
16977                             style : 'position: relative',
16978                             unselectable : 'on',
16979                             cn : [
16980                                 {
16981                                     cls : 'fc-event-container',
16982                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16983                                 },
16984                                 cal_table
16985                             ]
16986                         }
16987                     ]
16988     
16989                 }
16990            ] 
16991             
16992         };
16993         
16994          
16995         
16996         return cfg;
16997     },
16998     
16999     
17000     initEvents : function()
17001     {
17002         if(!this.store){
17003             throw "can not find store for calendar";
17004         }
17005         
17006         var mark = {
17007             tag: "div",
17008             cls:"x-dlg-mask",
17009             style: "text-align:center",
17010             cn: [
17011                 {
17012                     tag: "div",
17013                     style: "background-color:white;width:50%;margin:250 auto",
17014                     cn: [
17015                         {
17016                             tag: "img",
17017                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17018                         },
17019                         {
17020                             tag: "span",
17021                             html: "Loading"
17022                         }
17023                         
17024                     ]
17025                 }
17026             ]
17027         };
17028         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17029         
17030         var size = this.el.select('.fc-content', true).first().getSize();
17031         this.maskEl.setSize(size.width, size.height);
17032         this.maskEl.enableDisplayMode("block");
17033         if(!this.loadMask){
17034             this.maskEl.hide();
17035         }
17036         
17037         this.store = Roo.factory(this.store, Roo.data);
17038         this.store.on('load', this.onLoad, this);
17039         this.store.on('beforeload', this.onBeforeLoad, this);
17040         
17041         this.resize();
17042         
17043         this.cells = this.el.select('.fc-day',true);
17044         //Roo.log(this.cells);
17045         this.textNodes = this.el.query('.fc-day-number');
17046         this.cells.addClassOnOver('fc-state-hover');
17047         
17048         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17049         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17050         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17051         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17052         
17053         this.on('monthchange', this.onMonthChange, this);
17054         
17055         this.update(new Date().clearTime());
17056     },
17057     
17058     resize : function() {
17059         var sz  = this.el.getSize();
17060         
17061         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17062         this.el.select('.fc-day-content div',true).setHeight(34);
17063     },
17064     
17065     
17066     // private
17067     showPrevMonth : function(e){
17068         this.update(this.activeDate.add("mo", -1));
17069     },
17070     showToday : function(e){
17071         this.update(new Date().clearTime());
17072     },
17073     // private
17074     showNextMonth : function(e){
17075         this.update(this.activeDate.add("mo", 1));
17076     },
17077
17078     // private
17079     showPrevYear : function(){
17080         this.update(this.activeDate.add("y", -1));
17081     },
17082
17083     // private
17084     showNextYear : function(){
17085         this.update(this.activeDate.add("y", 1));
17086     },
17087
17088     
17089    // private
17090     update : function(date)
17091     {
17092         var vd = this.activeDate;
17093         this.activeDate = date;
17094 //        if(vd && this.el){
17095 //            var t = date.getTime();
17096 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17097 //                Roo.log('using add remove');
17098 //                
17099 //                this.fireEvent('monthchange', this, date);
17100 //                
17101 //                this.cells.removeClass("fc-state-highlight");
17102 //                this.cells.each(function(c){
17103 //                   if(c.dateValue == t){
17104 //                       c.addClass("fc-state-highlight");
17105 //                       setTimeout(function(){
17106 //                            try{c.dom.firstChild.focus();}catch(e){}
17107 //                       }, 50);
17108 //                       return false;
17109 //                   }
17110 //                   return true;
17111 //                });
17112 //                return;
17113 //            }
17114 //        }
17115         
17116         var days = date.getDaysInMonth();
17117         
17118         var firstOfMonth = date.getFirstDateOfMonth();
17119         var startingPos = firstOfMonth.getDay()-this.startDay;
17120         
17121         if(startingPos < this.startDay){
17122             startingPos += 7;
17123         }
17124         
17125         var pm = date.add(Date.MONTH, -1);
17126         var prevStart = pm.getDaysInMonth()-startingPos;
17127 //        
17128         this.cells = this.el.select('.fc-day',true);
17129         this.textNodes = this.el.query('.fc-day-number');
17130         this.cells.addClassOnOver('fc-state-hover');
17131         
17132         var cells = this.cells.elements;
17133         var textEls = this.textNodes;
17134         
17135         Roo.each(cells, function(cell){
17136             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17137         });
17138         
17139         days += startingPos;
17140
17141         // convert everything to numbers so it's fast
17142         var day = 86400000;
17143         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17144         //Roo.log(d);
17145         //Roo.log(pm);
17146         //Roo.log(prevStart);
17147         
17148         var today = new Date().clearTime().getTime();
17149         var sel = date.clearTime().getTime();
17150         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17151         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17152         var ddMatch = this.disabledDatesRE;
17153         var ddText = this.disabledDatesText;
17154         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17155         var ddaysText = this.disabledDaysText;
17156         var format = this.format;
17157         
17158         var setCellClass = function(cal, cell){
17159             cell.row = 0;
17160             cell.events = [];
17161             cell.more = [];
17162             //Roo.log('set Cell Class');
17163             cell.title = "";
17164             var t = d.getTime();
17165             
17166             //Roo.log(d);
17167             
17168             cell.dateValue = t;
17169             if(t == today){
17170                 cell.className += " fc-today";
17171                 cell.className += " fc-state-highlight";
17172                 cell.title = cal.todayText;
17173             }
17174             if(t == sel){
17175                 // disable highlight in other month..
17176                 //cell.className += " fc-state-highlight";
17177                 
17178             }
17179             // disabling
17180             if(t < min) {
17181                 cell.className = " fc-state-disabled";
17182                 cell.title = cal.minText;
17183                 return;
17184             }
17185             if(t > max) {
17186                 cell.className = " fc-state-disabled";
17187                 cell.title = cal.maxText;
17188                 return;
17189             }
17190             if(ddays){
17191                 if(ddays.indexOf(d.getDay()) != -1){
17192                     cell.title = ddaysText;
17193                     cell.className = " fc-state-disabled";
17194                 }
17195             }
17196             if(ddMatch && format){
17197                 var fvalue = d.dateFormat(format);
17198                 if(ddMatch.test(fvalue)){
17199                     cell.title = ddText.replace("%0", fvalue);
17200                     cell.className = " fc-state-disabled";
17201                 }
17202             }
17203             
17204             if (!cell.initialClassName) {
17205                 cell.initialClassName = cell.dom.className;
17206             }
17207             
17208             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17209         };
17210
17211         var i = 0;
17212         
17213         for(; i < startingPos; i++) {
17214             textEls[i].innerHTML = (++prevStart);
17215             d.setDate(d.getDate()+1);
17216             
17217             cells[i].className = "fc-past fc-other-month";
17218             setCellClass(this, cells[i]);
17219         }
17220         
17221         var intDay = 0;
17222         
17223         for(; i < days; i++){
17224             intDay = i - startingPos + 1;
17225             textEls[i].innerHTML = (intDay);
17226             d.setDate(d.getDate()+1);
17227             
17228             cells[i].className = ''; // "x-date-active";
17229             setCellClass(this, cells[i]);
17230         }
17231         var extraDays = 0;
17232         
17233         for(; i < 42; i++) {
17234             textEls[i].innerHTML = (++extraDays);
17235             d.setDate(d.getDate()+1);
17236             
17237             cells[i].className = "fc-future fc-other-month";
17238             setCellClass(this, cells[i]);
17239         }
17240         
17241         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17242         
17243         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17244         
17245         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17246         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17247         
17248         if(totalRows != 6){
17249             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17250             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17251         }
17252         
17253         this.fireEvent('monthchange', this, date);
17254         
17255         
17256         /*
17257         if(!this.internalRender){
17258             var main = this.el.dom.firstChild;
17259             var w = main.offsetWidth;
17260             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17261             Roo.fly(main).setWidth(w);
17262             this.internalRender = true;
17263             // opera does not respect the auto grow header center column
17264             // then, after it gets a width opera refuses to recalculate
17265             // without a second pass
17266             if(Roo.isOpera && !this.secondPass){
17267                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17268                 this.secondPass = true;
17269                 this.update.defer(10, this, [date]);
17270             }
17271         }
17272         */
17273         
17274     },
17275     
17276     findCell : function(dt) {
17277         dt = dt.clearTime().getTime();
17278         var ret = false;
17279         this.cells.each(function(c){
17280             //Roo.log("check " +c.dateValue + '?=' + dt);
17281             if(c.dateValue == dt){
17282                 ret = c;
17283                 return false;
17284             }
17285             return true;
17286         });
17287         
17288         return ret;
17289     },
17290     
17291     findCells : function(ev) {
17292         var s = ev.start.clone().clearTime().getTime();
17293        // Roo.log(s);
17294         var e= ev.end.clone().clearTime().getTime();
17295        // Roo.log(e);
17296         var ret = [];
17297         this.cells.each(function(c){
17298              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17299             
17300             if(c.dateValue > e){
17301                 return ;
17302             }
17303             if(c.dateValue < s){
17304                 return ;
17305             }
17306             ret.push(c);
17307         });
17308         
17309         return ret;    
17310     },
17311     
17312 //    findBestRow: function(cells)
17313 //    {
17314 //        var ret = 0;
17315 //        
17316 //        for (var i =0 ; i < cells.length;i++) {
17317 //            ret  = Math.max(cells[i].rows || 0,ret);
17318 //        }
17319 //        return ret;
17320 //        
17321 //    },
17322     
17323     
17324     addItem : function(ev)
17325     {
17326         // look for vertical location slot in
17327         var cells = this.findCells(ev);
17328         
17329 //        ev.row = this.findBestRow(cells);
17330         
17331         // work out the location.
17332         
17333         var crow = false;
17334         var rows = [];
17335         for(var i =0; i < cells.length; i++) {
17336             
17337             cells[i].row = cells[0].row;
17338             
17339             if(i == 0){
17340                 cells[i].row = cells[i].row + 1;
17341             }
17342             
17343             if (!crow) {
17344                 crow = {
17345                     start : cells[i],
17346                     end :  cells[i]
17347                 };
17348                 continue;
17349             }
17350             if (crow.start.getY() == cells[i].getY()) {
17351                 // on same row.
17352                 crow.end = cells[i];
17353                 continue;
17354             }
17355             // different row.
17356             rows.push(crow);
17357             crow = {
17358                 start: cells[i],
17359                 end : cells[i]
17360             };
17361             
17362         }
17363         
17364         rows.push(crow);
17365         ev.els = [];
17366         ev.rows = rows;
17367         ev.cells = cells;
17368         
17369         cells[0].events.push(ev);
17370         
17371         this.calevents.push(ev);
17372     },
17373     
17374     clearEvents: function() {
17375         
17376         if(!this.calevents){
17377             return;
17378         }
17379         
17380         Roo.each(this.cells.elements, function(c){
17381             c.row = 0;
17382             c.events = [];
17383             c.more = [];
17384         });
17385         
17386         Roo.each(this.calevents, function(e) {
17387             Roo.each(e.els, function(el) {
17388                 el.un('mouseenter' ,this.onEventEnter, this);
17389                 el.un('mouseleave' ,this.onEventLeave, this);
17390                 el.remove();
17391             },this);
17392         },this);
17393         
17394         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17395             e.remove();
17396         });
17397         
17398     },
17399     
17400     renderEvents: function()
17401     {   
17402         var _this = this;
17403         
17404         this.cells.each(function(c) {
17405             
17406             if(c.row < 5){
17407                 return;
17408             }
17409             
17410             var ev = c.events;
17411             
17412             var r = 4;
17413             if(c.row != c.events.length){
17414                 r = 4 - (4 - (c.row - c.events.length));
17415             }
17416             
17417             c.events = ev.slice(0, r);
17418             c.more = ev.slice(r);
17419             
17420             if(c.more.length && c.more.length == 1){
17421                 c.events.push(c.more.pop());
17422             }
17423             
17424             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17425             
17426         });
17427             
17428         this.cells.each(function(c) {
17429             
17430             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17431             
17432             
17433             for (var e = 0; e < c.events.length; e++){
17434                 var ev = c.events[e];
17435                 var rows = ev.rows;
17436                 
17437                 for(var i = 0; i < rows.length; i++) {
17438                 
17439                     // how many rows should it span..
17440
17441                     var  cfg = {
17442                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17443                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17444
17445                         unselectable : "on",
17446                         cn : [
17447                             {
17448                                 cls: 'fc-event-inner',
17449                                 cn : [
17450     //                                {
17451     //                                  tag:'span',
17452     //                                  cls: 'fc-event-time',
17453     //                                  html : cells.length > 1 ? '' : ev.time
17454     //                                },
17455                                     {
17456                                       tag:'span',
17457                                       cls: 'fc-event-title',
17458                                       html : String.format('{0}', ev.title)
17459                                     }
17460
17461
17462                                 ]
17463                             },
17464                             {
17465                                 cls: 'ui-resizable-handle ui-resizable-e',
17466                                 html : '&nbsp;&nbsp;&nbsp'
17467                             }
17468
17469                         ]
17470                     };
17471
17472                     if (i == 0) {
17473                         cfg.cls += ' fc-event-start';
17474                     }
17475                     if ((i+1) == rows.length) {
17476                         cfg.cls += ' fc-event-end';
17477                     }
17478
17479                     var ctr = _this.el.select('.fc-event-container',true).first();
17480                     var cg = ctr.createChild(cfg);
17481
17482                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17483                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17484
17485                     var r = (c.more.length) ? 1 : 0;
17486                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17487                     cg.setWidth(ebox.right - sbox.x -2);
17488
17489                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17490                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17491                     cg.on('click', _this.onEventClick, _this, ev);
17492
17493                     ev.els.push(cg);
17494                     
17495                 }
17496                 
17497             }
17498             
17499             
17500             if(c.more.length){
17501                 var  cfg = {
17502                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17503                     style : 'position: absolute',
17504                     unselectable : "on",
17505                     cn : [
17506                         {
17507                             cls: 'fc-event-inner',
17508                             cn : [
17509                                 {
17510                                   tag:'span',
17511                                   cls: 'fc-event-title',
17512                                   html : 'More'
17513                                 }
17514
17515
17516                             ]
17517                         },
17518                         {
17519                             cls: 'ui-resizable-handle ui-resizable-e',
17520                             html : '&nbsp;&nbsp;&nbsp'
17521                         }
17522
17523                     ]
17524                 };
17525
17526                 var ctr = _this.el.select('.fc-event-container',true).first();
17527                 var cg = ctr.createChild(cfg);
17528
17529                 var sbox = c.select('.fc-day-content',true).first().getBox();
17530                 var ebox = c.select('.fc-day-content',true).first().getBox();
17531                 //Roo.log(cg);
17532                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17533                 cg.setWidth(ebox.right - sbox.x -2);
17534
17535                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17536                 
17537             }
17538             
17539         });
17540         
17541         
17542         
17543     },
17544     
17545     onEventEnter: function (e, el,event,d) {
17546         this.fireEvent('evententer', this, el, event);
17547     },
17548     
17549     onEventLeave: function (e, el,event,d) {
17550         this.fireEvent('eventleave', this, el, event);
17551     },
17552     
17553     onEventClick: function (e, el,event,d) {
17554         this.fireEvent('eventclick', this, el, event);
17555     },
17556     
17557     onMonthChange: function () {
17558         this.store.load();
17559     },
17560     
17561     onMoreEventClick: function(e, el, more)
17562     {
17563         var _this = this;
17564         
17565         this.calpopover.placement = 'right';
17566         this.calpopover.setTitle('More');
17567         
17568         this.calpopover.setContent('');
17569         
17570         var ctr = this.calpopover.el.select('.popover-content', true).first();
17571         
17572         Roo.each(more, function(m){
17573             var cfg = {
17574                 cls : 'fc-event-hori fc-event-draggable',
17575                 html : m.title
17576             };
17577             var cg = ctr.createChild(cfg);
17578             
17579             cg.on('click', _this.onEventClick, _this, m);
17580         });
17581         
17582         this.calpopover.show(el);
17583         
17584         
17585     },
17586     
17587     onLoad: function () 
17588     {   
17589         this.calevents = [];
17590         var cal = this;
17591         
17592         if(this.store.getCount() > 0){
17593             this.store.data.each(function(d){
17594                cal.addItem({
17595                     id : d.data.id,
17596                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17597                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17598                     time : d.data.start_time,
17599                     title : d.data.title,
17600                     description : d.data.description,
17601                     venue : d.data.venue
17602                 });
17603             });
17604         }
17605         
17606         this.renderEvents();
17607         
17608         if(this.calevents.length && this.loadMask){
17609             this.maskEl.hide();
17610         }
17611     },
17612     
17613     onBeforeLoad: function()
17614     {
17615         this.clearEvents();
17616         if(this.loadMask){
17617             this.maskEl.show();
17618         }
17619     }
17620 });
17621
17622  
17623  /*
17624  * - LGPL
17625  *
17626  * element
17627  * 
17628  */
17629
17630 /**
17631  * @class Roo.bootstrap.Popover
17632  * @extends Roo.bootstrap.Component
17633  * Bootstrap Popover class
17634  * @cfg {String} html contents of the popover   (or false to use children..)
17635  * @cfg {String} title of popover (or false to hide)
17636  * @cfg {String} placement how it is placed
17637  * @cfg {String} trigger click || hover (or false to trigger manually)
17638  * @cfg {String} over what (parent or false to trigger manually.)
17639  * @cfg {Number} delay - delay before showing
17640  
17641  * @constructor
17642  * Create a new Popover
17643  * @param {Object} config The config object
17644  */
17645
17646 Roo.bootstrap.Popover = function(config){
17647     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17648     
17649     this.addEvents({
17650         // raw events
17651          /**
17652          * @event show
17653          * After the popover show
17654          * 
17655          * @param {Roo.bootstrap.Popover} this
17656          */
17657         "show" : true,
17658         /**
17659          * @event hide
17660          * After the popover hide
17661          * 
17662          * @param {Roo.bootstrap.Popover} this
17663          */
17664         "hide" : true
17665     });
17666 };
17667
17668 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17669     
17670     title: 'Fill in a title',
17671     html: false,
17672     
17673     placement : 'right',
17674     trigger : 'hover', // hover
17675     
17676     delay : 0,
17677     
17678     over: 'parent',
17679     
17680     can_build_overlaid : false,
17681     
17682     getChildContainer : function()
17683     {
17684         return this.el.select('.popover-content',true).first();
17685     },
17686     
17687     getAutoCreate : function(){
17688          
17689         var cfg = {
17690            cls : 'popover roo-dynamic',
17691            style: 'display:block',
17692            cn : [
17693                 {
17694                     cls : 'arrow'
17695                 },
17696                 {
17697                     cls : 'popover-inner',
17698                     cn : [
17699                         {
17700                             tag: 'h3',
17701                             cls: 'popover-title popover-header',
17702                             html : this.title
17703                         },
17704                         {
17705                             cls : 'popover-content popover-body',
17706                             html : this.html
17707                         }
17708                     ]
17709                     
17710                 }
17711            ]
17712         };
17713         
17714         return cfg;
17715     },
17716     setTitle: function(str)
17717     {
17718         this.title = str;
17719         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17720     },
17721     setContent: function(str)
17722     {
17723         this.html = str;
17724         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17725     },
17726     // as it get's added to the bottom of the page.
17727     onRender : function(ct, position)
17728     {
17729         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17730         if(!this.el){
17731             var cfg = Roo.apply({},  this.getAutoCreate());
17732             cfg.id = Roo.id();
17733             
17734             if (this.cls) {
17735                 cfg.cls += ' ' + this.cls;
17736             }
17737             if (this.style) {
17738                 cfg.style = this.style;
17739             }
17740             //Roo.log("adding to ");
17741             this.el = Roo.get(document.body).createChild(cfg, position);
17742 //            Roo.log(this.el);
17743         }
17744         this.initEvents();
17745     },
17746     
17747     initEvents : function()
17748     {
17749         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17750         this.el.enableDisplayMode('block');
17751         this.el.hide();
17752         if (this.over === false) {
17753             return; 
17754         }
17755         if (this.triggers === false) {
17756             return;
17757         }
17758         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17759         var triggers = this.trigger ? this.trigger.split(' ') : [];
17760         Roo.each(triggers, function(trigger) {
17761         
17762             if (trigger == 'click') {
17763                 on_el.on('click', this.toggle, this);
17764             } else if (trigger != 'manual') {
17765                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17766                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17767       
17768                 on_el.on(eventIn  ,this.enter, this);
17769                 on_el.on(eventOut, this.leave, this);
17770             }
17771         }, this);
17772         
17773     },
17774     
17775     
17776     // private
17777     timeout : null,
17778     hoverState : null,
17779     
17780     toggle : function () {
17781         this.hoverState == 'in' ? this.leave() : this.enter();
17782     },
17783     
17784     enter : function () {
17785         
17786         clearTimeout(this.timeout);
17787     
17788         this.hoverState = 'in';
17789     
17790         if (!this.delay || !this.delay.show) {
17791             this.show();
17792             return;
17793         }
17794         var _t = this;
17795         this.timeout = setTimeout(function () {
17796             if (_t.hoverState == 'in') {
17797                 _t.show();
17798             }
17799         }, this.delay.show)
17800     },
17801     
17802     leave : function() {
17803         clearTimeout(this.timeout);
17804     
17805         this.hoverState = 'out';
17806     
17807         if (!this.delay || !this.delay.hide) {
17808             this.hide();
17809             return;
17810         }
17811         var _t = this;
17812         this.timeout = setTimeout(function () {
17813             if (_t.hoverState == 'out') {
17814                 _t.hide();
17815             }
17816         }, this.delay.hide)
17817     },
17818     
17819     show : function (on_el)
17820     {
17821         if (!on_el) {
17822             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17823         }
17824         
17825         // set content.
17826         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17827         if (this.html !== false) {
17828             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17829         }
17830         this.el.removeClass([
17831             'fade','top','bottom', 'left', 'right','in',
17832             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17833         ]);
17834         if (!this.title.length) {
17835             this.el.select('.popover-title',true).hide();
17836         }
17837         
17838         var placement = typeof this.placement == 'function' ?
17839             this.placement.call(this, this.el, on_el) :
17840             this.placement;
17841             
17842         var autoToken = /\s?auto?\s?/i;
17843         var autoPlace = autoToken.test(placement);
17844         if (autoPlace) {
17845             placement = placement.replace(autoToken, '') || 'top';
17846         }
17847         
17848         //this.el.detach()
17849         //this.el.setXY([0,0]);
17850         this.el.show();
17851         this.el.dom.style.display='block';
17852         this.el.addClass(placement);
17853         
17854         //this.el.appendTo(on_el);
17855         
17856         var p = this.getPosition();
17857         var box = this.el.getBox();
17858         
17859         if (autoPlace) {
17860             // fixme..
17861         }
17862         var align = Roo.bootstrap.Popover.alignment[placement];
17863         
17864 //        Roo.log(align);
17865         this.el.alignTo(on_el, align[0],align[1]);
17866         //var arrow = this.el.select('.arrow',true).first();
17867         //arrow.set(align[2], 
17868         
17869         this.el.addClass('in');
17870         
17871         
17872         if (this.el.hasClass('fade')) {
17873             // fade it?
17874         }
17875         
17876         this.hoverState = 'in';
17877         
17878         this.fireEvent('show', this);
17879         
17880     },
17881     hide : function()
17882     {
17883         this.el.setXY([0,0]);
17884         this.el.removeClass('in');
17885         this.el.hide();
17886         this.hoverState = null;
17887         
17888         this.fireEvent('hide', this);
17889     }
17890     
17891 });
17892
17893 Roo.bootstrap.Popover.alignment = {
17894     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17895     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17896     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17897     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17898 };
17899
17900  /*
17901  * - LGPL
17902  *
17903  * Progress
17904  * 
17905  */
17906
17907 /**
17908  * @class Roo.bootstrap.Progress
17909  * @extends Roo.bootstrap.Component
17910  * Bootstrap Progress class
17911  * @cfg {Boolean} striped striped of the progress bar
17912  * @cfg {Boolean} active animated of the progress bar
17913  * 
17914  * 
17915  * @constructor
17916  * Create a new Progress
17917  * @param {Object} config The config object
17918  */
17919
17920 Roo.bootstrap.Progress = function(config){
17921     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17922 };
17923
17924 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17925     
17926     striped : false,
17927     active: false,
17928     
17929     getAutoCreate : function(){
17930         var cfg = {
17931             tag: 'div',
17932             cls: 'progress'
17933         };
17934         
17935         
17936         if(this.striped){
17937             cfg.cls += ' progress-striped';
17938         }
17939       
17940         if(this.active){
17941             cfg.cls += ' active';
17942         }
17943         
17944         
17945         return cfg;
17946     }
17947    
17948 });
17949
17950  
17951
17952  /*
17953  * - LGPL
17954  *
17955  * ProgressBar
17956  * 
17957  */
17958
17959 /**
17960  * @class Roo.bootstrap.ProgressBar
17961  * @extends Roo.bootstrap.Component
17962  * Bootstrap ProgressBar class
17963  * @cfg {Number} aria_valuenow aria-value now
17964  * @cfg {Number} aria_valuemin aria-value min
17965  * @cfg {Number} aria_valuemax aria-value max
17966  * @cfg {String} label label for the progress bar
17967  * @cfg {String} panel (success | info | warning | danger )
17968  * @cfg {String} role role of the progress bar
17969  * @cfg {String} sr_only text
17970  * 
17971  * 
17972  * @constructor
17973  * Create a new ProgressBar
17974  * @param {Object} config The config object
17975  */
17976
17977 Roo.bootstrap.ProgressBar = function(config){
17978     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17979 };
17980
17981 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17982     
17983     aria_valuenow : 0,
17984     aria_valuemin : 0,
17985     aria_valuemax : 100,
17986     label : false,
17987     panel : false,
17988     role : false,
17989     sr_only: false,
17990     
17991     getAutoCreate : function()
17992     {
17993         
17994         var cfg = {
17995             tag: 'div',
17996             cls: 'progress-bar',
17997             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17998         };
17999         
18000         if(this.sr_only){
18001             cfg.cn = {
18002                 tag: 'span',
18003                 cls: 'sr-only',
18004                 html: this.sr_only
18005             }
18006         }
18007         
18008         if(this.role){
18009             cfg.role = this.role;
18010         }
18011         
18012         if(this.aria_valuenow){
18013             cfg['aria-valuenow'] = this.aria_valuenow;
18014         }
18015         
18016         if(this.aria_valuemin){
18017             cfg['aria-valuemin'] = this.aria_valuemin;
18018         }
18019         
18020         if(this.aria_valuemax){
18021             cfg['aria-valuemax'] = this.aria_valuemax;
18022         }
18023         
18024         if(this.label && !this.sr_only){
18025             cfg.html = this.label;
18026         }
18027         
18028         if(this.panel){
18029             cfg.cls += ' progress-bar-' + this.panel;
18030         }
18031         
18032         return cfg;
18033     },
18034     
18035     update : function(aria_valuenow)
18036     {
18037         this.aria_valuenow = aria_valuenow;
18038         
18039         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18040     }
18041    
18042 });
18043
18044  
18045
18046  /*
18047  * - LGPL
18048  *
18049  * column
18050  * 
18051  */
18052
18053 /**
18054  * @class Roo.bootstrap.TabGroup
18055  * @extends Roo.bootstrap.Column
18056  * Bootstrap Column class
18057  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18058  * @cfg {Boolean} carousel true to make the group behave like a carousel
18059  * @cfg {Boolean} bullets show bullets for the panels
18060  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18061  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18062  * @cfg {Boolean} showarrow (true|false) show arrow default true
18063  * 
18064  * @constructor
18065  * Create a new TabGroup
18066  * @param {Object} config The config object
18067  */
18068
18069 Roo.bootstrap.TabGroup = function(config){
18070     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18071     if (!this.navId) {
18072         this.navId = Roo.id();
18073     }
18074     this.tabs = [];
18075     Roo.bootstrap.TabGroup.register(this);
18076     
18077 };
18078
18079 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18080     
18081     carousel : false,
18082     transition : false,
18083     bullets : 0,
18084     timer : 0,
18085     autoslide : false,
18086     slideFn : false,
18087     slideOnTouch : false,
18088     showarrow : true,
18089     
18090     getAutoCreate : function()
18091     {
18092         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18093         
18094         cfg.cls += ' tab-content';
18095         
18096         if (this.carousel) {
18097             cfg.cls += ' carousel slide';
18098             
18099             cfg.cn = [{
18100                cls : 'carousel-inner',
18101                cn : []
18102             }];
18103         
18104             if(this.bullets  && !Roo.isTouch){
18105                 
18106                 var bullets = {
18107                     cls : 'carousel-bullets',
18108                     cn : []
18109                 };
18110                
18111                 if(this.bullets_cls){
18112                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18113                 }
18114                 
18115                 bullets.cn.push({
18116                     cls : 'clear'
18117                 });
18118                 
18119                 cfg.cn[0].cn.push(bullets);
18120             }
18121             
18122             if(this.showarrow){
18123                 cfg.cn[0].cn.push({
18124                     tag : 'div',
18125                     class : 'carousel-arrow',
18126                     cn : [
18127                         {
18128                             tag : 'div',
18129                             class : 'carousel-prev',
18130                             cn : [
18131                                 {
18132                                     tag : 'i',
18133                                     class : 'fa fa-chevron-left'
18134                                 }
18135                             ]
18136                         },
18137                         {
18138                             tag : 'div',
18139                             class : 'carousel-next',
18140                             cn : [
18141                                 {
18142                                     tag : 'i',
18143                                     class : 'fa fa-chevron-right'
18144                                 }
18145                             ]
18146                         }
18147                     ]
18148                 });
18149             }
18150             
18151         }
18152         
18153         return cfg;
18154     },
18155     
18156     initEvents:  function()
18157     {
18158 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18159 //            this.el.on("touchstart", this.onTouchStart, this);
18160 //        }
18161         
18162         if(this.autoslide){
18163             var _this = this;
18164             
18165             this.slideFn = window.setInterval(function() {
18166                 _this.showPanelNext();
18167             }, this.timer);
18168         }
18169         
18170         if(this.showarrow){
18171             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18172             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18173         }
18174         
18175         
18176     },
18177     
18178 //    onTouchStart : function(e, el, o)
18179 //    {
18180 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18181 //            return;
18182 //        }
18183 //        
18184 //        this.showPanelNext();
18185 //    },
18186     
18187     
18188     getChildContainer : function()
18189     {
18190         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18191     },
18192     
18193     /**
18194     * register a Navigation item
18195     * @param {Roo.bootstrap.NavItem} the navitem to add
18196     */
18197     register : function(item)
18198     {
18199         this.tabs.push( item);
18200         item.navId = this.navId; // not really needed..
18201         this.addBullet();
18202     
18203     },
18204     
18205     getActivePanel : function()
18206     {
18207         var r = false;
18208         Roo.each(this.tabs, function(t) {
18209             if (t.active) {
18210                 r = t;
18211                 return false;
18212             }
18213             return null;
18214         });
18215         return r;
18216         
18217     },
18218     getPanelByName : function(n)
18219     {
18220         var r = false;
18221         Roo.each(this.tabs, function(t) {
18222             if (t.tabId == n) {
18223                 r = t;
18224                 return false;
18225             }
18226             return null;
18227         });
18228         return r;
18229     },
18230     indexOfPanel : function(p)
18231     {
18232         var r = false;
18233         Roo.each(this.tabs, function(t,i) {
18234             if (t.tabId == p.tabId) {
18235                 r = i;
18236                 return false;
18237             }
18238             return null;
18239         });
18240         return r;
18241     },
18242     /**
18243      * show a specific panel
18244      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18245      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18246      */
18247     showPanel : function (pan)
18248     {
18249         if(this.transition || typeof(pan) == 'undefined'){
18250             Roo.log("waiting for the transitionend");
18251             return;
18252         }
18253         
18254         if (typeof(pan) == 'number') {
18255             pan = this.tabs[pan];
18256         }
18257         
18258         if (typeof(pan) == 'string') {
18259             pan = this.getPanelByName(pan);
18260         }
18261         
18262         var cur = this.getActivePanel();
18263         
18264         if(!pan || !cur){
18265             Roo.log('pan or acitve pan is undefined');
18266             return false;
18267         }
18268         
18269         if (pan.tabId == this.getActivePanel().tabId) {
18270             return true;
18271         }
18272         
18273         if (false === cur.fireEvent('beforedeactivate')) {
18274             return false;
18275         }
18276         
18277         if(this.bullets > 0 && !Roo.isTouch){
18278             this.setActiveBullet(this.indexOfPanel(pan));
18279         }
18280         
18281         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18282             
18283             this.transition = true;
18284             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18285             var lr = dir == 'next' ? 'left' : 'right';
18286             pan.el.addClass(dir); // or prev
18287             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18288             cur.el.addClass(lr); // or right
18289             pan.el.addClass(lr);
18290             
18291             var _this = this;
18292             cur.el.on('transitionend', function() {
18293                 Roo.log("trans end?");
18294                 
18295                 pan.el.removeClass([lr,dir]);
18296                 pan.setActive(true);
18297                 
18298                 cur.el.removeClass([lr]);
18299                 cur.setActive(false);
18300                 
18301                 _this.transition = false;
18302                 
18303             }, this, { single:  true } );
18304             
18305             return true;
18306         }
18307         
18308         cur.setActive(false);
18309         pan.setActive(true);
18310         
18311         return true;
18312         
18313     },
18314     showPanelNext : function()
18315     {
18316         var i = this.indexOfPanel(this.getActivePanel());
18317         
18318         if (i >= this.tabs.length - 1 && !this.autoslide) {
18319             return;
18320         }
18321         
18322         if (i >= this.tabs.length - 1 && this.autoslide) {
18323             i = -1;
18324         }
18325         
18326         this.showPanel(this.tabs[i+1]);
18327     },
18328     
18329     showPanelPrev : function()
18330     {
18331         var i = this.indexOfPanel(this.getActivePanel());
18332         
18333         if (i  < 1 && !this.autoslide) {
18334             return;
18335         }
18336         
18337         if (i < 1 && this.autoslide) {
18338             i = this.tabs.length;
18339         }
18340         
18341         this.showPanel(this.tabs[i-1]);
18342     },
18343     
18344     
18345     addBullet: function()
18346     {
18347         if(!this.bullets || Roo.isTouch){
18348             return;
18349         }
18350         var ctr = this.el.select('.carousel-bullets',true).first();
18351         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18352         var bullet = ctr.createChild({
18353             cls : 'bullet bullet-' + i
18354         },ctr.dom.lastChild);
18355         
18356         
18357         var _this = this;
18358         
18359         bullet.on('click', (function(e, el, o, ii, t){
18360
18361             e.preventDefault();
18362
18363             this.showPanel(ii);
18364
18365             if(this.autoslide && this.slideFn){
18366                 clearInterval(this.slideFn);
18367                 this.slideFn = window.setInterval(function() {
18368                     _this.showPanelNext();
18369                 }, this.timer);
18370             }
18371
18372         }).createDelegate(this, [i, bullet], true));
18373                 
18374         
18375     },
18376      
18377     setActiveBullet : function(i)
18378     {
18379         if(Roo.isTouch){
18380             return;
18381         }
18382         
18383         Roo.each(this.el.select('.bullet', true).elements, function(el){
18384             el.removeClass('selected');
18385         });
18386
18387         var bullet = this.el.select('.bullet-' + i, true).first();
18388         
18389         if(!bullet){
18390             return;
18391         }
18392         
18393         bullet.addClass('selected');
18394     }
18395     
18396     
18397   
18398 });
18399
18400  
18401
18402  
18403  
18404 Roo.apply(Roo.bootstrap.TabGroup, {
18405     
18406     groups: {},
18407      /**
18408     * register a Navigation Group
18409     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18410     */
18411     register : function(navgrp)
18412     {
18413         this.groups[navgrp.navId] = navgrp;
18414         
18415     },
18416     /**
18417     * fetch a Navigation Group based on the navigation ID
18418     * if one does not exist , it will get created.
18419     * @param {string} the navgroup to add
18420     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18421     */
18422     get: function(navId) {
18423         if (typeof(this.groups[navId]) == 'undefined') {
18424             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18425         }
18426         return this.groups[navId] ;
18427     }
18428     
18429     
18430     
18431 });
18432
18433  /*
18434  * - LGPL
18435  *
18436  * TabPanel
18437  * 
18438  */
18439
18440 /**
18441  * @class Roo.bootstrap.TabPanel
18442  * @extends Roo.bootstrap.Component
18443  * Bootstrap TabPanel class
18444  * @cfg {Boolean} active panel active
18445  * @cfg {String} html panel content
18446  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18447  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18448  * @cfg {String} href click to link..
18449  * 
18450  * 
18451  * @constructor
18452  * Create a new TabPanel
18453  * @param {Object} config The config object
18454  */
18455
18456 Roo.bootstrap.TabPanel = function(config){
18457     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18458     this.addEvents({
18459         /**
18460              * @event changed
18461              * Fires when the active status changes
18462              * @param {Roo.bootstrap.TabPanel} this
18463              * @param {Boolean} state the new state
18464             
18465          */
18466         'changed': true,
18467         /**
18468              * @event beforedeactivate
18469              * Fires before a tab is de-activated - can be used to do validation on a form.
18470              * @param {Roo.bootstrap.TabPanel} this
18471              * @return {Boolean} false if there is an error
18472             
18473          */
18474         'beforedeactivate': true
18475      });
18476     
18477     this.tabId = this.tabId || Roo.id();
18478   
18479 };
18480
18481 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18482     
18483     active: false,
18484     html: false,
18485     tabId: false,
18486     navId : false,
18487     href : '',
18488     
18489     getAutoCreate : function(){
18490         var cfg = {
18491             tag: 'div',
18492             // item is needed for carousel - not sure if it has any effect otherwise
18493             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18494             html: this.html || ''
18495         };
18496         
18497         if(this.active){
18498             cfg.cls += ' active';
18499         }
18500         
18501         if(this.tabId){
18502             cfg.tabId = this.tabId;
18503         }
18504         
18505         
18506         return cfg;
18507     },
18508     
18509     initEvents:  function()
18510     {
18511         var p = this.parent();
18512         
18513         this.navId = this.navId || p.navId;
18514         
18515         if (typeof(this.navId) != 'undefined') {
18516             // not really needed.. but just in case.. parent should be a NavGroup.
18517             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18518             
18519             tg.register(this);
18520             
18521             var i = tg.tabs.length - 1;
18522             
18523             if(this.active && tg.bullets > 0 && i < tg.bullets){
18524                 tg.setActiveBullet(i);
18525             }
18526         }
18527         
18528         this.el.on('click', this.onClick, this);
18529         
18530         if(Roo.isTouch){
18531             this.el.on("touchstart", this.onTouchStart, this);
18532             this.el.on("touchmove", this.onTouchMove, this);
18533             this.el.on("touchend", this.onTouchEnd, this);
18534         }
18535         
18536     },
18537     
18538     onRender : function(ct, position)
18539     {
18540         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18541     },
18542     
18543     setActive : function(state)
18544     {
18545         Roo.log("panel - set active " + this.tabId + "=" + state);
18546         
18547         this.active = state;
18548         if (!state) {
18549             this.el.removeClass('active');
18550             
18551         } else  if (!this.el.hasClass('active')) {
18552             this.el.addClass('active');
18553         }
18554         
18555         this.fireEvent('changed', this, state);
18556     },
18557     
18558     onClick : function(e)
18559     {
18560         e.preventDefault();
18561         
18562         if(!this.href.length){
18563             return;
18564         }
18565         
18566         window.location.href = this.href;
18567     },
18568     
18569     startX : 0,
18570     startY : 0,
18571     endX : 0,
18572     endY : 0,
18573     swiping : false,
18574     
18575     onTouchStart : function(e)
18576     {
18577         this.swiping = false;
18578         
18579         this.startX = e.browserEvent.touches[0].clientX;
18580         this.startY = e.browserEvent.touches[0].clientY;
18581     },
18582     
18583     onTouchMove : function(e)
18584     {
18585         this.swiping = true;
18586         
18587         this.endX = e.browserEvent.touches[0].clientX;
18588         this.endY = e.browserEvent.touches[0].clientY;
18589     },
18590     
18591     onTouchEnd : function(e)
18592     {
18593         if(!this.swiping){
18594             this.onClick(e);
18595             return;
18596         }
18597         
18598         var tabGroup = this.parent();
18599         
18600         if(this.endX > this.startX){ // swiping right
18601             tabGroup.showPanelPrev();
18602             return;
18603         }
18604         
18605         if(this.startX > this.endX){ // swiping left
18606             tabGroup.showPanelNext();
18607             return;
18608         }
18609     }
18610     
18611     
18612 });
18613  
18614
18615  
18616
18617  /*
18618  * - LGPL
18619  *
18620  * DateField
18621  * 
18622  */
18623
18624 /**
18625  * @class Roo.bootstrap.DateField
18626  * @extends Roo.bootstrap.Input
18627  * Bootstrap DateField class
18628  * @cfg {Number} weekStart default 0
18629  * @cfg {String} viewMode default empty, (months|years)
18630  * @cfg {String} minViewMode default empty, (months|years)
18631  * @cfg {Number} startDate default -Infinity
18632  * @cfg {Number} endDate default Infinity
18633  * @cfg {Boolean} todayHighlight default false
18634  * @cfg {Boolean} todayBtn default false
18635  * @cfg {Boolean} calendarWeeks default false
18636  * @cfg {Object} daysOfWeekDisabled default empty
18637  * @cfg {Boolean} singleMode default false (true | false)
18638  * 
18639  * @cfg {Boolean} keyboardNavigation default true
18640  * @cfg {String} language default en
18641  * 
18642  * @constructor
18643  * Create a new DateField
18644  * @param {Object} config The config object
18645  */
18646
18647 Roo.bootstrap.DateField = function(config){
18648     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18649      this.addEvents({
18650             /**
18651              * @event show
18652              * Fires when this field show.
18653              * @param {Roo.bootstrap.DateField} this
18654              * @param {Mixed} date The date value
18655              */
18656             show : true,
18657             /**
18658              * @event show
18659              * Fires when this field hide.
18660              * @param {Roo.bootstrap.DateField} this
18661              * @param {Mixed} date The date value
18662              */
18663             hide : true,
18664             /**
18665              * @event select
18666              * Fires when select a date.
18667              * @param {Roo.bootstrap.DateField} this
18668              * @param {Mixed} date The date value
18669              */
18670             select : true,
18671             /**
18672              * @event beforeselect
18673              * Fires when before select a date.
18674              * @param {Roo.bootstrap.DateField} this
18675              * @param {Mixed} date The date value
18676              */
18677             beforeselect : true
18678         });
18679 };
18680
18681 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18682     
18683     /**
18684      * @cfg {String} format
18685      * The default date format string which can be overriden for localization support.  The format must be
18686      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18687      */
18688     format : "m/d/y",
18689     /**
18690      * @cfg {String} altFormats
18691      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18692      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18693      */
18694     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18695     
18696     weekStart : 0,
18697     
18698     viewMode : '',
18699     
18700     minViewMode : '',
18701     
18702     todayHighlight : false,
18703     
18704     todayBtn: false,
18705     
18706     language: 'en',
18707     
18708     keyboardNavigation: true,
18709     
18710     calendarWeeks: false,
18711     
18712     startDate: -Infinity,
18713     
18714     endDate: Infinity,
18715     
18716     daysOfWeekDisabled: [],
18717     
18718     _events: [],
18719     
18720     singleMode : false,
18721     
18722     UTCDate: function()
18723     {
18724         return new Date(Date.UTC.apply(Date, arguments));
18725     },
18726     
18727     UTCToday: function()
18728     {
18729         var today = new Date();
18730         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18731     },
18732     
18733     getDate: function() {
18734             var d = this.getUTCDate();
18735             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18736     },
18737     
18738     getUTCDate: function() {
18739             return this.date;
18740     },
18741     
18742     setDate: function(d) {
18743             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18744     },
18745     
18746     setUTCDate: function(d) {
18747             this.date = d;
18748             this.setValue(this.formatDate(this.date));
18749     },
18750         
18751     onRender: function(ct, position)
18752     {
18753         
18754         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18755         
18756         this.language = this.language || 'en';
18757         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18758         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18759         
18760         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18761         this.format = this.format || 'm/d/y';
18762         this.isInline = false;
18763         this.isInput = true;
18764         this.component = this.el.select('.add-on', true).first() || false;
18765         this.component = (this.component && this.component.length === 0) ? false : this.component;
18766         this.hasInput = this.component && this.inputEl().length;
18767         
18768         if (typeof(this.minViewMode === 'string')) {
18769             switch (this.minViewMode) {
18770                 case 'months':
18771                     this.minViewMode = 1;
18772                     break;
18773                 case 'years':
18774                     this.minViewMode = 2;
18775                     break;
18776                 default:
18777                     this.minViewMode = 0;
18778                     break;
18779             }
18780         }
18781         
18782         if (typeof(this.viewMode === 'string')) {
18783             switch (this.viewMode) {
18784                 case 'months':
18785                     this.viewMode = 1;
18786                     break;
18787                 case 'years':
18788                     this.viewMode = 2;
18789                     break;
18790                 default:
18791                     this.viewMode = 0;
18792                     break;
18793             }
18794         }
18795                 
18796         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18797         
18798 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18799         
18800         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18801         
18802         this.picker().on('mousedown', this.onMousedown, this);
18803         this.picker().on('click', this.onClick, this);
18804         
18805         this.picker().addClass('datepicker-dropdown');
18806         
18807         this.startViewMode = this.viewMode;
18808         
18809         if(this.singleMode){
18810             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18811                 v.setVisibilityMode(Roo.Element.DISPLAY);
18812                 v.hide();
18813             });
18814             
18815             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18816                 v.setStyle('width', '189px');
18817             });
18818         }
18819         
18820         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18821             if(!this.calendarWeeks){
18822                 v.remove();
18823                 return;
18824             }
18825             
18826             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18827             v.attr('colspan', function(i, val){
18828                 return parseInt(val) + 1;
18829             });
18830         });
18831                         
18832         
18833         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18834         
18835         this.setStartDate(this.startDate);
18836         this.setEndDate(this.endDate);
18837         
18838         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18839         
18840         this.fillDow();
18841         this.fillMonths();
18842         this.update();
18843         this.showMode();
18844         
18845         if(this.isInline) {
18846             this.showPopup();
18847         }
18848     },
18849     
18850     picker : function()
18851     {
18852         return this.pickerEl;
18853 //        return this.el.select('.datepicker', true).first();
18854     },
18855     
18856     fillDow: function()
18857     {
18858         var dowCnt = this.weekStart;
18859         
18860         var dow = {
18861             tag: 'tr',
18862             cn: [
18863                 
18864             ]
18865         };
18866         
18867         if(this.calendarWeeks){
18868             dow.cn.push({
18869                 tag: 'th',
18870                 cls: 'cw',
18871                 html: '&nbsp;'
18872             })
18873         }
18874         
18875         while (dowCnt < this.weekStart + 7) {
18876             dow.cn.push({
18877                 tag: 'th',
18878                 cls: 'dow',
18879                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18880             });
18881         }
18882         
18883         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18884     },
18885     
18886     fillMonths: function()
18887     {    
18888         var i = 0;
18889         var months = this.picker().select('>.datepicker-months td', true).first();
18890         
18891         months.dom.innerHTML = '';
18892         
18893         while (i < 12) {
18894             var month = {
18895                 tag: 'span',
18896                 cls: 'month',
18897                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18898             };
18899             
18900             months.createChild(month);
18901         }
18902         
18903     },
18904     
18905     update: function()
18906     {
18907         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;
18908         
18909         if (this.date < this.startDate) {
18910             this.viewDate = new Date(this.startDate);
18911         } else if (this.date > this.endDate) {
18912             this.viewDate = new Date(this.endDate);
18913         } else {
18914             this.viewDate = new Date(this.date);
18915         }
18916         
18917         this.fill();
18918     },
18919     
18920     fill: function() 
18921     {
18922         var d = new Date(this.viewDate),
18923                 year = d.getUTCFullYear(),
18924                 month = d.getUTCMonth(),
18925                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18926                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18927                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18928                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18929                 currentDate = this.date && this.date.valueOf(),
18930                 today = this.UTCToday();
18931         
18932         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18933         
18934 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18935         
18936 //        this.picker.select('>tfoot th.today').
18937 //                                              .text(dates[this.language].today)
18938 //                                              .toggle(this.todayBtn !== false);
18939     
18940         this.updateNavArrows();
18941         this.fillMonths();
18942                                                 
18943         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18944         
18945         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18946          
18947         prevMonth.setUTCDate(day);
18948         
18949         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18950         
18951         var nextMonth = new Date(prevMonth);
18952         
18953         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18954         
18955         nextMonth = nextMonth.valueOf();
18956         
18957         var fillMonths = false;
18958         
18959         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18960         
18961         while(prevMonth.valueOf() <= nextMonth) {
18962             var clsName = '';
18963             
18964             if (prevMonth.getUTCDay() === this.weekStart) {
18965                 if(fillMonths){
18966                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18967                 }
18968                     
18969                 fillMonths = {
18970                     tag: 'tr',
18971                     cn: []
18972                 };
18973                 
18974                 if(this.calendarWeeks){
18975                     // ISO 8601: First week contains first thursday.
18976                     // ISO also states week starts on Monday, but we can be more abstract here.
18977                     var
18978                     // Start of current week: based on weekstart/current date
18979                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18980                     // Thursday of this week
18981                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18982                     // First Thursday of year, year from thursday
18983                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18984                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18985                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18986                     
18987                     fillMonths.cn.push({
18988                         tag: 'td',
18989                         cls: 'cw',
18990                         html: calWeek
18991                     });
18992                 }
18993             }
18994             
18995             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18996                 clsName += ' old';
18997             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18998                 clsName += ' new';
18999             }
19000             if (this.todayHighlight &&
19001                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19002                 prevMonth.getUTCMonth() == today.getMonth() &&
19003                 prevMonth.getUTCDate() == today.getDate()) {
19004                 clsName += ' today';
19005             }
19006             
19007             if (currentDate && prevMonth.valueOf() === currentDate) {
19008                 clsName += ' active';
19009             }
19010             
19011             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19012                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19013                     clsName += ' disabled';
19014             }
19015             
19016             fillMonths.cn.push({
19017                 tag: 'td',
19018                 cls: 'day ' + clsName,
19019                 html: prevMonth.getDate()
19020             });
19021             
19022             prevMonth.setDate(prevMonth.getDate()+1);
19023         }
19024           
19025         var currentYear = this.date && this.date.getUTCFullYear();
19026         var currentMonth = this.date && this.date.getUTCMonth();
19027         
19028         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19029         
19030         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19031             v.removeClass('active');
19032             
19033             if(currentYear === year && k === currentMonth){
19034                 v.addClass('active');
19035             }
19036             
19037             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19038                 v.addClass('disabled');
19039             }
19040             
19041         });
19042         
19043         
19044         year = parseInt(year/10, 10) * 10;
19045         
19046         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19047         
19048         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19049         
19050         year -= 1;
19051         for (var i = -1; i < 11; i++) {
19052             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19053                 tag: 'span',
19054                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19055                 html: year
19056             });
19057             
19058             year += 1;
19059         }
19060     },
19061     
19062     showMode: function(dir) 
19063     {
19064         if (dir) {
19065             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19066         }
19067         
19068         Roo.each(this.picker().select('>div',true).elements, function(v){
19069             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19070             v.hide();
19071         });
19072         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19073     },
19074     
19075     place: function()
19076     {
19077         if(this.isInline) {
19078             return;
19079         }
19080         
19081         this.picker().removeClass(['bottom', 'top']);
19082         
19083         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19084             /*
19085              * place to the top of element!
19086              *
19087              */
19088             
19089             this.picker().addClass('top');
19090             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19091             
19092             return;
19093         }
19094         
19095         this.picker().addClass('bottom');
19096         
19097         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19098     },
19099     
19100     parseDate : function(value)
19101     {
19102         if(!value || value instanceof Date){
19103             return value;
19104         }
19105         var v = Date.parseDate(value, this.format);
19106         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19107             v = Date.parseDate(value, 'Y-m-d');
19108         }
19109         if(!v && this.altFormats){
19110             if(!this.altFormatsArray){
19111                 this.altFormatsArray = this.altFormats.split("|");
19112             }
19113             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19114                 v = Date.parseDate(value, this.altFormatsArray[i]);
19115             }
19116         }
19117         return v;
19118     },
19119     
19120     formatDate : function(date, fmt)
19121     {   
19122         return (!date || !(date instanceof Date)) ?
19123         date : date.dateFormat(fmt || this.format);
19124     },
19125     
19126     onFocus : function()
19127     {
19128         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19129         this.showPopup();
19130     },
19131     
19132     onBlur : function()
19133     {
19134         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19135         
19136         var d = this.inputEl().getValue();
19137         
19138         this.setValue(d);
19139                 
19140         this.hidePopup();
19141     },
19142     
19143     showPopup : function()
19144     {
19145         this.picker().show();
19146         this.update();
19147         this.place();
19148         
19149         this.fireEvent('showpopup', this, this.date);
19150     },
19151     
19152     hidePopup : function()
19153     {
19154         if(this.isInline) {
19155             return;
19156         }
19157         this.picker().hide();
19158         this.viewMode = this.startViewMode;
19159         this.showMode();
19160         
19161         this.fireEvent('hidepopup', this, this.date);
19162         
19163     },
19164     
19165     onMousedown: function(e)
19166     {
19167         e.stopPropagation();
19168         e.preventDefault();
19169     },
19170     
19171     keyup: function(e)
19172     {
19173         Roo.bootstrap.DateField.superclass.keyup.call(this);
19174         this.update();
19175     },
19176
19177     setValue: function(v)
19178     {
19179         if(this.fireEvent('beforeselect', this, v) !== false){
19180             var d = new Date(this.parseDate(v) ).clearTime();
19181         
19182             if(isNaN(d.getTime())){
19183                 this.date = this.viewDate = '';
19184                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19185                 return;
19186             }
19187
19188             v = this.formatDate(d);
19189
19190             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19191
19192             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19193
19194             this.update();
19195
19196             this.fireEvent('select', this, this.date);
19197         }
19198     },
19199     
19200     getValue: function()
19201     {
19202         return this.formatDate(this.date);
19203     },
19204     
19205     fireKey: function(e)
19206     {
19207         if (!this.picker().isVisible()){
19208             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19209                 this.showPopup();
19210             }
19211             return;
19212         }
19213         
19214         var dateChanged = false,
19215         dir, day, month,
19216         newDate, newViewDate;
19217         
19218         switch(e.keyCode){
19219             case 27: // escape
19220                 this.hidePopup();
19221                 e.preventDefault();
19222                 break;
19223             case 37: // left
19224             case 39: // right
19225                 if (!this.keyboardNavigation) {
19226                     break;
19227                 }
19228                 dir = e.keyCode == 37 ? -1 : 1;
19229                 
19230                 if (e.ctrlKey){
19231                     newDate = this.moveYear(this.date, dir);
19232                     newViewDate = this.moveYear(this.viewDate, dir);
19233                 } else if (e.shiftKey){
19234                     newDate = this.moveMonth(this.date, dir);
19235                     newViewDate = this.moveMonth(this.viewDate, dir);
19236                 } else {
19237                     newDate = new Date(this.date);
19238                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19239                     newViewDate = new Date(this.viewDate);
19240                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19241                 }
19242                 if (this.dateWithinRange(newDate)){
19243                     this.date = newDate;
19244                     this.viewDate = newViewDate;
19245                     this.setValue(this.formatDate(this.date));
19246 //                    this.update();
19247                     e.preventDefault();
19248                     dateChanged = true;
19249                 }
19250                 break;
19251             case 38: // up
19252             case 40: // down
19253                 if (!this.keyboardNavigation) {
19254                     break;
19255                 }
19256                 dir = e.keyCode == 38 ? -1 : 1;
19257                 if (e.ctrlKey){
19258                     newDate = this.moveYear(this.date, dir);
19259                     newViewDate = this.moveYear(this.viewDate, dir);
19260                 } else if (e.shiftKey){
19261                     newDate = this.moveMonth(this.date, dir);
19262                     newViewDate = this.moveMonth(this.viewDate, dir);
19263                 } else {
19264                     newDate = new Date(this.date);
19265                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19266                     newViewDate = new Date(this.viewDate);
19267                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19268                 }
19269                 if (this.dateWithinRange(newDate)){
19270                     this.date = newDate;
19271                     this.viewDate = newViewDate;
19272                     this.setValue(this.formatDate(this.date));
19273 //                    this.update();
19274                     e.preventDefault();
19275                     dateChanged = true;
19276                 }
19277                 break;
19278             case 13: // enter
19279                 this.setValue(this.formatDate(this.date));
19280                 this.hidePopup();
19281                 e.preventDefault();
19282                 break;
19283             case 9: // tab
19284                 this.setValue(this.formatDate(this.date));
19285                 this.hidePopup();
19286                 break;
19287             case 16: // shift
19288             case 17: // ctrl
19289             case 18: // alt
19290                 break;
19291             default :
19292                 this.hidePopup();
19293                 
19294         }
19295     },
19296     
19297     
19298     onClick: function(e) 
19299     {
19300         e.stopPropagation();
19301         e.preventDefault();
19302         
19303         var target = e.getTarget();
19304         
19305         if(target.nodeName.toLowerCase() === 'i'){
19306             target = Roo.get(target).dom.parentNode;
19307         }
19308         
19309         var nodeName = target.nodeName;
19310         var className = target.className;
19311         var html = target.innerHTML;
19312         //Roo.log(nodeName);
19313         
19314         switch(nodeName.toLowerCase()) {
19315             case 'th':
19316                 switch(className) {
19317                     case 'switch':
19318                         this.showMode(1);
19319                         break;
19320                     case 'prev':
19321                     case 'next':
19322                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19323                         switch(this.viewMode){
19324                                 case 0:
19325                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19326                                         break;
19327                                 case 1:
19328                                 case 2:
19329                                         this.viewDate = this.moveYear(this.viewDate, dir);
19330                                         break;
19331                         }
19332                         this.fill();
19333                         break;
19334                     case 'today':
19335                         var date = new Date();
19336                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19337 //                        this.fill()
19338                         this.setValue(this.formatDate(this.date));
19339                         
19340                         this.hidePopup();
19341                         break;
19342                 }
19343                 break;
19344             case 'span':
19345                 if (className.indexOf('disabled') < 0) {
19346                     this.viewDate.setUTCDate(1);
19347                     if (className.indexOf('month') > -1) {
19348                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19349                     } else {
19350                         var year = parseInt(html, 10) || 0;
19351                         this.viewDate.setUTCFullYear(year);
19352                         
19353                     }
19354                     
19355                     if(this.singleMode){
19356                         this.setValue(this.formatDate(this.viewDate));
19357                         this.hidePopup();
19358                         return;
19359                     }
19360                     
19361                     this.showMode(-1);
19362                     this.fill();
19363                 }
19364                 break;
19365                 
19366             case 'td':
19367                 //Roo.log(className);
19368                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19369                     var day = parseInt(html, 10) || 1;
19370                     var year = this.viewDate.getUTCFullYear(),
19371                         month = this.viewDate.getUTCMonth();
19372
19373                     if (className.indexOf('old') > -1) {
19374                         if(month === 0 ){
19375                             month = 11;
19376                             year -= 1;
19377                         }else{
19378                             month -= 1;
19379                         }
19380                     } else if (className.indexOf('new') > -1) {
19381                         if (month == 11) {
19382                             month = 0;
19383                             year += 1;
19384                         } else {
19385                             month += 1;
19386                         }
19387                     }
19388                     //Roo.log([year,month,day]);
19389                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19390                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19391 //                    this.fill();
19392                     //Roo.log(this.formatDate(this.date));
19393                     this.setValue(this.formatDate(this.date));
19394                     this.hidePopup();
19395                 }
19396                 break;
19397         }
19398     },
19399     
19400     setStartDate: function(startDate)
19401     {
19402         this.startDate = startDate || -Infinity;
19403         if (this.startDate !== -Infinity) {
19404             this.startDate = this.parseDate(this.startDate);
19405         }
19406         this.update();
19407         this.updateNavArrows();
19408     },
19409
19410     setEndDate: function(endDate)
19411     {
19412         this.endDate = endDate || Infinity;
19413         if (this.endDate !== Infinity) {
19414             this.endDate = this.parseDate(this.endDate);
19415         }
19416         this.update();
19417         this.updateNavArrows();
19418     },
19419     
19420     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19421     {
19422         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19423         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19424             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19425         }
19426         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19427             return parseInt(d, 10);
19428         });
19429         this.update();
19430         this.updateNavArrows();
19431     },
19432     
19433     updateNavArrows: function() 
19434     {
19435         if(this.singleMode){
19436             return;
19437         }
19438         
19439         var d = new Date(this.viewDate),
19440         year = d.getUTCFullYear(),
19441         month = d.getUTCMonth();
19442         
19443         Roo.each(this.picker().select('.prev', true).elements, function(v){
19444             v.show();
19445             switch (this.viewMode) {
19446                 case 0:
19447
19448                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19449                         v.hide();
19450                     }
19451                     break;
19452                 case 1:
19453                 case 2:
19454                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19455                         v.hide();
19456                     }
19457                     break;
19458             }
19459         });
19460         
19461         Roo.each(this.picker().select('.next', true).elements, function(v){
19462             v.show();
19463             switch (this.viewMode) {
19464                 case 0:
19465
19466                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19467                         v.hide();
19468                     }
19469                     break;
19470                 case 1:
19471                 case 2:
19472                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19473                         v.hide();
19474                     }
19475                     break;
19476             }
19477         })
19478     },
19479     
19480     moveMonth: function(date, dir)
19481     {
19482         if (!dir) {
19483             return date;
19484         }
19485         var new_date = new Date(date.valueOf()),
19486         day = new_date.getUTCDate(),
19487         month = new_date.getUTCMonth(),
19488         mag = Math.abs(dir),
19489         new_month, test;
19490         dir = dir > 0 ? 1 : -1;
19491         if (mag == 1){
19492             test = dir == -1
19493             // If going back one month, make sure month is not current month
19494             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19495             ? function(){
19496                 return new_date.getUTCMonth() == month;
19497             }
19498             // If going forward one month, make sure month is as expected
19499             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19500             : function(){
19501                 return new_date.getUTCMonth() != new_month;
19502             };
19503             new_month = month + dir;
19504             new_date.setUTCMonth(new_month);
19505             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19506             if (new_month < 0 || new_month > 11) {
19507                 new_month = (new_month + 12) % 12;
19508             }
19509         } else {
19510             // For magnitudes >1, move one month at a time...
19511             for (var i=0; i<mag; i++) {
19512                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19513                 new_date = this.moveMonth(new_date, dir);
19514             }
19515             // ...then reset the day, keeping it in the new month
19516             new_month = new_date.getUTCMonth();
19517             new_date.setUTCDate(day);
19518             test = function(){
19519                 return new_month != new_date.getUTCMonth();
19520             };
19521         }
19522         // Common date-resetting loop -- if date is beyond end of month, make it
19523         // end of month
19524         while (test()){
19525             new_date.setUTCDate(--day);
19526             new_date.setUTCMonth(new_month);
19527         }
19528         return new_date;
19529     },
19530
19531     moveYear: function(date, dir)
19532     {
19533         return this.moveMonth(date, dir*12);
19534     },
19535
19536     dateWithinRange: function(date)
19537     {
19538         return date >= this.startDate && date <= this.endDate;
19539     },
19540
19541     
19542     remove: function() 
19543     {
19544         this.picker().remove();
19545     },
19546     
19547     validateValue : function(value)
19548     {
19549         if(this.getVisibilityEl().hasClass('hidden')){
19550             return true;
19551         }
19552         
19553         if(value.length < 1)  {
19554             if(this.allowBlank){
19555                 return true;
19556             }
19557             return false;
19558         }
19559         
19560         if(value.length < this.minLength){
19561             return false;
19562         }
19563         if(value.length > this.maxLength){
19564             return false;
19565         }
19566         if(this.vtype){
19567             var vt = Roo.form.VTypes;
19568             if(!vt[this.vtype](value, this)){
19569                 return false;
19570             }
19571         }
19572         if(typeof this.validator == "function"){
19573             var msg = this.validator(value);
19574             if(msg !== true){
19575                 return false;
19576             }
19577         }
19578         
19579         if(this.regex && !this.regex.test(value)){
19580             return false;
19581         }
19582         
19583         if(typeof(this.parseDate(value)) == 'undefined'){
19584             return false;
19585         }
19586         
19587         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19588             return false;
19589         }      
19590         
19591         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19592             return false;
19593         } 
19594         
19595         
19596         return true;
19597     },
19598     
19599     reset : function()
19600     {
19601         this.date = this.viewDate = '';
19602         
19603         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19604     }
19605    
19606 });
19607
19608 Roo.apply(Roo.bootstrap.DateField,  {
19609     
19610     head : {
19611         tag: 'thead',
19612         cn: [
19613         {
19614             tag: 'tr',
19615             cn: [
19616             {
19617                 tag: 'th',
19618                 cls: 'prev',
19619                 html: '<i class="fa fa-arrow-left"/>'
19620             },
19621             {
19622                 tag: 'th',
19623                 cls: 'switch',
19624                 colspan: '5'
19625             },
19626             {
19627                 tag: 'th',
19628                 cls: 'next',
19629                 html: '<i class="fa fa-arrow-right"/>'
19630             }
19631
19632             ]
19633         }
19634         ]
19635     },
19636     
19637     content : {
19638         tag: 'tbody',
19639         cn: [
19640         {
19641             tag: 'tr',
19642             cn: [
19643             {
19644                 tag: 'td',
19645                 colspan: '7'
19646             }
19647             ]
19648         }
19649         ]
19650     },
19651     
19652     footer : {
19653         tag: 'tfoot',
19654         cn: [
19655         {
19656             tag: 'tr',
19657             cn: [
19658             {
19659                 tag: 'th',
19660                 colspan: '7',
19661                 cls: 'today'
19662             }
19663                     
19664             ]
19665         }
19666         ]
19667     },
19668     
19669     dates:{
19670         en: {
19671             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19672             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19673             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19674             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19675             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19676             today: "Today"
19677         }
19678     },
19679     
19680     modes: [
19681     {
19682         clsName: 'days',
19683         navFnc: 'Month',
19684         navStep: 1
19685     },
19686     {
19687         clsName: 'months',
19688         navFnc: 'FullYear',
19689         navStep: 1
19690     },
19691     {
19692         clsName: 'years',
19693         navFnc: 'FullYear',
19694         navStep: 10
19695     }]
19696 });
19697
19698 Roo.apply(Roo.bootstrap.DateField,  {
19699   
19700     template : {
19701         tag: 'div',
19702         cls: 'datepicker dropdown-menu roo-dynamic',
19703         cn: [
19704         {
19705             tag: 'div',
19706             cls: 'datepicker-days',
19707             cn: [
19708             {
19709                 tag: 'table',
19710                 cls: 'table-condensed',
19711                 cn:[
19712                 Roo.bootstrap.DateField.head,
19713                 {
19714                     tag: 'tbody'
19715                 },
19716                 Roo.bootstrap.DateField.footer
19717                 ]
19718             }
19719             ]
19720         },
19721         {
19722             tag: 'div',
19723             cls: 'datepicker-months',
19724             cn: [
19725             {
19726                 tag: 'table',
19727                 cls: 'table-condensed',
19728                 cn:[
19729                 Roo.bootstrap.DateField.head,
19730                 Roo.bootstrap.DateField.content,
19731                 Roo.bootstrap.DateField.footer
19732                 ]
19733             }
19734             ]
19735         },
19736         {
19737             tag: 'div',
19738             cls: 'datepicker-years',
19739             cn: [
19740             {
19741                 tag: 'table',
19742                 cls: 'table-condensed',
19743                 cn:[
19744                 Roo.bootstrap.DateField.head,
19745                 Roo.bootstrap.DateField.content,
19746                 Roo.bootstrap.DateField.footer
19747                 ]
19748             }
19749             ]
19750         }
19751         ]
19752     }
19753 });
19754
19755  
19756
19757  /*
19758  * - LGPL
19759  *
19760  * TimeField
19761  * 
19762  */
19763
19764 /**
19765  * @class Roo.bootstrap.TimeField
19766  * @extends Roo.bootstrap.Input
19767  * Bootstrap DateField class
19768  * 
19769  * 
19770  * @constructor
19771  * Create a new TimeField
19772  * @param {Object} config The config object
19773  */
19774
19775 Roo.bootstrap.TimeField = function(config){
19776     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19777     this.addEvents({
19778             /**
19779              * @event show
19780              * Fires when this field show.
19781              * @param {Roo.bootstrap.DateField} thisthis
19782              * @param {Mixed} date The date value
19783              */
19784             show : true,
19785             /**
19786              * @event show
19787              * Fires when this field hide.
19788              * @param {Roo.bootstrap.DateField} this
19789              * @param {Mixed} date The date value
19790              */
19791             hide : true,
19792             /**
19793              * @event select
19794              * Fires when select a date.
19795              * @param {Roo.bootstrap.DateField} this
19796              * @param {Mixed} date The date value
19797              */
19798             select : true
19799         });
19800 };
19801
19802 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19803     
19804     /**
19805      * @cfg {String} format
19806      * The default time format string which can be overriden for localization support.  The format must be
19807      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19808      */
19809     format : "H:i",
19810        
19811     onRender: function(ct, position)
19812     {
19813         
19814         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19815                 
19816         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19817         
19818         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19819         
19820         this.pop = this.picker().select('>.datepicker-time',true).first();
19821         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19822         
19823         this.picker().on('mousedown', this.onMousedown, this);
19824         this.picker().on('click', this.onClick, this);
19825         
19826         this.picker().addClass('datepicker-dropdown');
19827     
19828         this.fillTime();
19829         this.update();
19830             
19831         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19832         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19833         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19834         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19835         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19836         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19837
19838     },
19839     
19840     fireKey: function(e){
19841         if (!this.picker().isVisible()){
19842             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19843                 this.show();
19844             }
19845             return;
19846         }
19847
19848         e.preventDefault();
19849         
19850         switch(e.keyCode){
19851             case 27: // escape
19852                 this.hide();
19853                 break;
19854             case 37: // left
19855             case 39: // right
19856                 this.onTogglePeriod();
19857                 break;
19858             case 38: // up
19859                 this.onIncrementMinutes();
19860                 break;
19861             case 40: // down
19862                 this.onDecrementMinutes();
19863                 break;
19864             case 13: // enter
19865             case 9: // tab
19866                 this.setTime();
19867                 break;
19868         }
19869     },
19870     
19871     onClick: function(e) {
19872         e.stopPropagation();
19873         e.preventDefault();
19874     },
19875     
19876     picker : function()
19877     {
19878         return this.el.select('.datepicker', true).first();
19879     },
19880     
19881     fillTime: function()
19882     {    
19883         var time = this.pop.select('tbody', true).first();
19884         
19885         time.dom.innerHTML = '';
19886         
19887         time.createChild({
19888             tag: 'tr',
19889             cn: [
19890                 {
19891                     tag: 'td',
19892                     cn: [
19893                         {
19894                             tag: 'a',
19895                             href: '#',
19896                             cls: 'btn',
19897                             cn: [
19898                                 {
19899                                     tag: 'span',
19900                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19901                                 }
19902                             ]
19903                         } 
19904                     ]
19905                 },
19906                 {
19907                     tag: 'td',
19908                     cls: 'separator'
19909                 },
19910                 {
19911                     tag: 'td',
19912                     cn: [
19913                         {
19914                             tag: 'a',
19915                             href: '#',
19916                             cls: 'btn',
19917                             cn: [
19918                                 {
19919                                     tag: 'span',
19920                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19921                                 }
19922                             ]
19923                         }
19924                     ]
19925                 },
19926                 {
19927                     tag: 'td',
19928                     cls: 'separator'
19929                 }
19930             ]
19931         });
19932         
19933         time.createChild({
19934             tag: 'tr',
19935             cn: [
19936                 {
19937                     tag: 'td',
19938                     cn: [
19939                         {
19940                             tag: 'span',
19941                             cls: 'timepicker-hour',
19942                             html: '00'
19943                         }  
19944                     ]
19945                 },
19946                 {
19947                     tag: 'td',
19948                     cls: 'separator',
19949                     html: ':'
19950                 },
19951                 {
19952                     tag: 'td',
19953                     cn: [
19954                         {
19955                             tag: 'span',
19956                             cls: 'timepicker-minute',
19957                             html: '00'
19958                         }  
19959                     ]
19960                 },
19961                 {
19962                     tag: 'td',
19963                     cls: 'separator'
19964                 },
19965                 {
19966                     tag: 'td',
19967                     cn: [
19968                         {
19969                             tag: 'button',
19970                             type: 'button',
19971                             cls: 'btn btn-primary period',
19972                             html: 'AM'
19973                             
19974                         }
19975                     ]
19976                 }
19977             ]
19978         });
19979         
19980         time.createChild({
19981             tag: 'tr',
19982             cn: [
19983                 {
19984                     tag: 'td',
19985                     cn: [
19986                         {
19987                             tag: 'a',
19988                             href: '#',
19989                             cls: 'btn',
19990                             cn: [
19991                                 {
19992                                     tag: 'span',
19993                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19994                                 }
19995                             ]
19996                         }
19997                     ]
19998                 },
19999                 {
20000                     tag: 'td',
20001                     cls: 'separator'
20002                 },
20003                 {
20004                     tag: 'td',
20005                     cn: [
20006                         {
20007                             tag: 'a',
20008                             href: '#',
20009                             cls: 'btn',
20010                             cn: [
20011                                 {
20012                                     tag: 'span',
20013                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20014                                 }
20015                             ]
20016                         }
20017                     ]
20018                 },
20019                 {
20020                     tag: 'td',
20021                     cls: 'separator'
20022                 }
20023             ]
20024         });
20025         
20026     },
20027     
20028     update: function()
20029     {
20030         
20031         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20032         
20033         this.fill();
20034     },
20035     
20036     fill: function() 
20037     {
20038         var hours = this.time.getHours();
20039         var minutes = this.time.getMinutes();
20040         var period = 'AM';
20041         
20042         if(hours > 11){
20043             period = 'PM';
20044         }
20045         
20046         if(hours == 0){
20047             hours = 12;
20048         }
20049         
20050         
20051         if(hours > 12){
20052             hours = hours - 12;
20053         }
20054         
20055         if(hours < 10){
20056             hours = '0' + hours;
20057         }
20058         
20059         if(minutes < 10){
20060             minutes = '0' + minutes;
20061         }
20062         
20063         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20064         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20065         this.pop.select('button', true).first().dom.innerHTML = period;
20066         
20067     },
20068     
20069     place: function()
20070     {   
20071         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20072         
20073         var cls = ['bottom'];
20074         
20075         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20076             cls.pop();
20077             cls.push('top');
20078         }
20079         
20080         cls.push('right');
20081         
20082         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20083             cls.pop();
20084             cls.push('left');
20085         }
20086         
20087         this.picker().addClass(cls.join('-'));
20088         
20089         var _this = this;
20090         
20091         Roo.each(cls, function(c){
20092             if(c == 'bottom'){
20093                 _this.picker().setTop(_this.inputEl().getHeight());
20094                 return;
20095             }
20096             if(c == 'top'){
20097                 _this.picker().setTop(0 - _this.picker().getHeight());
20098                 return;
20099             }
20100             
20101             if(c == 'left'){
20102                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20103                 return;
20104             }
20105             if(c == 'right'){
20106                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20107                 return;
20108             }
20109         });
20110         
20111     },
20112   
20113     onFocus : function()
20114     {
20115         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20116         this.show();
20117     },
20118     
20119     onBlur : function()
20120     {
20121         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20122         this.hide();
20123     },
20124     
20125     show : function()
20126     {
20127         this.picker().show();
20128         this.pop.show();
20129         this.update();
20130         this.place();
20131         
20132         this.fireEvent('show', this, this.date);
20133     },
20134     
20135     hide : function()
20136     {
20137         this.picker().hide();
20138         this.pop.hide();
20139         
20140         this.fireEvent('hide', this, this.date);
20141     },
20142     
20143     setTime : function()
20144     {
20145         this.hide();
20146         this.setValue(this.time.format(this.format));
20147         
20148         this.fireEvent('select', this, this.date);
20149         
20150         
20151     },
20152     
20153     onMousedown: function(e){
20154         e.stopPropagation();
20155         e.preventDefault();
20156     },
20157     
20158     onIncrementHours: function()
20159     {
20160         Roo.log('onIncrementHours');
20161         this.time = this.time.add(Date.HOUR, 1);
20162         this.update();
20163         
20164     },
20165     
20166     onDecrementHours: function()
20167     {
20168         Roo.log('onDecrementHours');
20169         this.time = this.time.add(Date.HOUR, -1);
20170         this.update();
20171     },
20172     
20173     onIncrementMinutes: function()
20174     {
20175         Roo.log('onIncrementMinutes');
20176         this.time = this.time.add(Date.MINUTE, 1);
20177         this.update();
20178     },
20179     
20180     onDecrementMinutes: function()
20181     {
20182         Roo.log('onDecrementMinutes');
20183         this.time = this.time.add(Date.MINUTE, -1);
20184         this.update();
20185     },
20186     
20187     onTogglePeriod: function()
20188     {
20189         Roo.log('onTogglePeriod');
20190         this.time = this.time.add(Date.HOUR, 12);
20191         this.update();
20192     }
20193     
20194    
20195 });
20196
20197 Roo.apply(Roo.bootstrap.TimeField,  {
20198     
20199     content : {
20200         tag: 'tbody',
20201         cn: [
20202             {
20203                 tag: 'tr',
20204                 cn: [
20205                 {
20206                     tag: 'td',
20207                     colspan: '7'
20208                 }
20209                 ]
20210             }
20211         ]
20212     },
20213     
20214     footer : {
20215         tag: 'tfoot',
20216         cn: [
20217             {
20218                 tag: 'tr',
20219                 cn: [
20220                 {
20221                     tag: 'th',
20222                     colspan: '7',
20223                     cls: '',
20224                     cn: [
20225                         {
20226                             tag: 'button',
20227                             cls: 'btn btn-info ok',
20228                             html: 'OK'
20229                         }
20230                     ]
20231                 }
20232
20233                 ]
20234             }
20235         ]
20236     }
20237 });
20238
20239 Roo.apply(Roo.bootstrap.TimeField,  {
20240   
20241     template : {
20242         tag: 'div',
20243         cls: 'datepicker dropdown-menu',
20244         cn: [
20245             {
20246                 tag: 'div',
20247                 cls: 'datepicker-time',
20248                 cn: [
20249                 {
20250                     tag: 'table',
20251                     cls: 'table-condensed',
20252                     cn:[
20253                     Roo.bootstrap.TimeField.content,
20254                     Roo.bootstrap.TimeField.footer
20255                     ]
20256                 }
20257                 ]
20258             }
20259         ]
20260     }
20261 });
20262
20263  
20264
20265  /*
20266  * - LGPL
20267  *
20268  * MonthField
20269  * 
20270  */
20271
20272 /**
20273  * @class Roo.bootstrap.MonthField
20274  * @extends Roo.bootstrap.Input
20275  * Bootstrap MonthField class
20276  * 
20277  * @cfg {String} language default en
20278  * 
20279  * @constructor
20280  * Create a new MonthField
20281  * @param {Object} config The config object
20282  */
20283
20284 Roo.bootstrap.MonthField = function(config){
20285     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20286     
20287     this.addEvents({
20288         /**
20289          * @event show
20290          * Fires when this field show.
20291          * @param {Roo.bootstrap.MonthField} this
20292          * @param {Mixed} date The date value
20293          */
20294         show : true,
20295         /**
20296          * @event show
20297          * Fires when this field hide.
20298          * @param {Roo.bootstrap.MonthField} this
20299          * @param {Mixed} date The date value
20300          */
20301         hide : true,
20302         /**
20303          * @event select
20304          * Fires when select a date.
20305          * @param {Roo.bootstrap.MonthField} this
20306          * @param {String} oldvalue The old value
20307          * @param {String} newvalue The new value
20308          */
20309         select : true
20310     });
20311 };
20312
20313 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20314     
20315     onRender: function(ct, position)
20316     {
20317         
20318         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20319         
20320         this.language = this.language || 'en';
20321         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20322         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20323         
20324         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20325         this.isInline = false;
20326         this.isInput = true;
20327         this.component = this.el.select('.add-on', true).first() || false;
20328         this.component = (this.component && this.component.length === 0) ? false : this.component;
20329         this.hasInput = this.component && this.inputEL().length;
20330         
20331         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20332         
20333         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20334         
20335         this.picker().on('mousedown', this.onMousedown, this);
20336         this.picker().on('click', this.onClick, this);
20337         
20338         this.picker().addClass('datepicker-dropdown');
20339         
20340         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20341             v.setStyle('width', '189px');
20342         });
20343         
20344         this.fillMonths();
20345         
20346         this.update();
20347         
20348         if(this.isInline) {
20349             this.show();
20350         }
20351         
20352     },
20353     
20354     setValue: function(v, suppressEvent)
20355     {   
20356         var o = this.getValue();
20357         
20358         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20359         
20360         this.update();
20361
20362         if(suppressEvent !== true){
20363             this.fireEvent('select', this, o, v);
20364         }
20365         
20366     },
20367     
20368     getValue: function()
20369     {
20370         return this.value;
20371     },
20372     
20373     onClick: function(e) 
20374     {
20375         e.stopPropagation();
20376         e.preventDefault();
20377         
20378         var target = e.getTarget();
20379         
20380         if(target.nodeName.toLowerCase() === 'i'){
20381             target = Roo.get(target).dom.parentNode;
20382         }
20383         
20384         var nodeName = target.nodeName;
20385         var className = target.className;
20386         var html = target.innerHTML;
20387         
20388         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20389             return;
20390         }
20391         
20392         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20393         
20394         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20395         
20396         this.hide();
20397                         
20398     },
20399     
20400     picker : function()
20401     {
20402         return this.pickerEl;
20403     },
20404     
20405     fillMonths: function()
20406     {    
20407         var i = 0;
20408         var months = this.picker().select('>.datepicker-months td', true).first();
20409         
20410         months.dom.innerHTML = '';
20411         
20412         while (i < 12) {
20413             var month = {
20414                 tag: 'span',
20415                 cls: 'month',
20416                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20417             };
20418             
20419             months.createChild(month);
20420         }
20421         
20422     },
20423     
20424     update: function()
20425     {
20426         var _this = this;
20427         
20428         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20429             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20430         }
20431         
20432         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20433             e.removeClass('active');
20434             
20435             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20436                 e.addClass('active');
20437             }
20438         })
20439     },
20440     
20441     place: function()
20442     {
20443         if(this.isInline) {
20444             return;
20445         }
20446         
20447         this.picker().removeClass(['bottom', 'top']);
20448         
20449         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20450             /*
20451              * place to the top of element!
20452              *
20453              */
20454             
20455             this.picker().addClass('top');
20456             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20457             
20458             return;
20459         }
20460         
20461         this.picker().addClass('bottom');
20462         
20463         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20464     },
20465     
20466     onFocus : function()
20467     {
20468         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20469         this.show();
20470     },
20471     
20472     onBlur : function()
20473     {
20474         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20475         
20476         var d = this.inputEl().getValue();
20477         
20478         this.setValue(d);
20479                 
20480         this.hide();
20481     },
20482     
20483     show : function()
20484     {
20485         this.picker().show();
20486         this.picker().select('>.datepicker-months', true).first().show();
20487         this.update();
20488         this.place();
20489         
20490         this.fireEvent('show', this, this.date);
20491     },
20492     
20493     hide : function()
20494     {
20495         if(this.isInline) {
20496             return;
20497         }
20498         this.picker().hide();
20499         this.fireEvent('hide', this, this.date);
20500         
20501     },
20502     
20503     onMousedown: function(e)
20504     {
20505         e.stopPropagation();
20506         e.preventDefault();
20507     },
20508     
20509     keyup: function(e)
20510     {
20511         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20512         this.update();
20513     },
20514
20515     fireKey: function(e)
20516     {
20517         if (!this.picker().isVisible()){
20518             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20519                 this.show();
20520             }
20521             return;
20522         }
20523         
20524         var dir;
20525         
20526         switch(e.keyCode){
20527             case 27: // escape
20528                 this.hide();
20529                 e.preventDefault();
20530                 break;
20531             case 37: // left
20532             case 39: // right
20533                 dir = e.keyCode == 37 ? -1 : 1;
20534                 
20535                 this.vIndex = this.vIndex + dir;
20536                 
20537                 if(this.vIndex < 0){
20538                     this.vIndex = 0;
20539                 }
20540                 
20541                 if(this.vIndex > 11){
20542                     this.vIndex = 11;
20543                 }
20544                 
20545                 if(isNaN(this.vIndex)){
20546                     this.vIndex = 0;
20547                 }
20548                 
20549                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20550                 
20551                 break;
20552             case 38: // up
20553             case 40: // down
20554                 
20555                 dir = e.keyCode == 38 ? -1 : 1;
20556                 
20557                 this.vIndex = this.vIndex + dir * 4;
20558                 
20559                 if(this.vIndex < 0){
20560                     this.vIndex = 0;
20561                 }
20562                 
20563                 if(this.vIndex > 11){
20564                     this.vIndex = 11;
20565                 }
20566                 
20567                 if(isNaN(this.vIndex)){
20568                     this.vIndex = 0;
20569                 }
20570                 
20571                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20572                 break;
20573                 
20574             case 13: // enter
20575                 
20576                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20577                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20578                 }
20579                 
20580                 this.hide();
20581                 e.preventDefault();
20582                 break;
20583             case 9: // tab
20584                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20585                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20586                 }
20587                 this.hide();
20588                 break;
20589             case 16: // shift
20590             case 17: // ctrl
20591             case 18: // alt
20592                 break;
20593             default :
20594                 this.hide();
20595                 
20596         }
20597     },
20598     
20599     remove: function() 
20600     {
20601         this.picker().remove();
20602     }
20603    
20604 });
20605
20606 Roo.apply(Roo.bootstrap.MonthField,  {
20607     
20608     content : {
20609         tag: 'tbody',
20610         cn: [
20611         {
20612             tag: 'tr',
20613             cn: [
20614             {
20615                 tag: 'td',
20616                 colspan: '7'
20617             }
20618             ]
20619         }
20620         ]
20621     },
20622     
20623     dates:{
20624         en: {
20625             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20626             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20627         }
20628     }
20629 });
20630
20631 Roo.apply(Roo.bootstrap.MonthField,  {
20632   
20633     template : {
20634         tag: 'div',
20635         cls: 'datepicker dropdown-menu roo-dynamic',
20636         cn: [
20637             {
20638                 tag: 'div',
20639                 cls: 'datepicker-months',
20640                 cn: [
20641                 {
20642                     tag: 'table',
20643                     cls: 'table-condensed',
20644                     cn:[
20645                         Roo.bootstrap.DateField.content
20646                     ]
20647                 }
20648                 ]
20649             }
20650         ]
20651     }
20652 });
20653
20654  
20655
20656  
20657  /*
20658  * - LGPL
20659  *
20660  * CheckBox
20661  * 
20662  */
20663
20664 /**
20665  * @class Roo.bootstrap.CheckBox
20666  * @extends Roo.bootstrap.Input
20667  * Bootstrap CheckBox class
20668  * 
20669  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20670  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20671  * @cfg {String} boxLabel The text that appears beside the checkbox
20672  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20673  * @cfg {Boolean} checked initnal the element
20674  * @cfg {Boolean} inline inline the element (default false)
20675  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20676  * @cfg {String} tooltip label tooltip
20677  * 
20678  * @constructor
20679  * Create a new CheckBox
20680  * @param {Object} config The config object
20681  */
20682
20683 Roo.bootstrap.CheckBox = function(config){
20684     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20685    
20686     this.addEvents({
20687         /**
20688         * @event check
20689         * Fires when the element is checked or unchecked.
20690         * @param {Roo.bootstrap.CheckBox} this This input
20691         * @param {Boolean} checked The new checked value
20692         */
20693        check : true,
20694        /**
20695         * @event click
20696         * Fires when the element is click.
20697         * @param {Roo.bootstrap.CheckBox} this This input
20698         */
20699        click : true
20700     });
20701     
20702 };
20703
20704 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20705   
20706     inputType: 'checkbox',
20707     inputValue: 1,
20708     valueOff: 0,
20709     boxLabel: false,
20710     checked: false,
20711     weight : false,
20712     inline: false,
20713     tooltip : '',
20714     
20715     getAutoCreate : function()
20716     {
20717         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20718         
20719         var id = Roo.id();
20720         
20721         var cfg = {};
20722         
20723         cfg.cls = 'form-group ' + this.inputType; //input-group
20724         
20725         if(this.inline){
20726             cfg.cls += ' ' + this.inputType + '-inline';
20727         }
20728         
20729         var input =  {
20730             tag: 'input',
20731             id : id,
20732             type : this.inputType,
20733             value : this.inputValue,
20734             cls : 'roo-' + this.inputType, //'form-box',
20735             placeholder : this.placeholder || ''
20736             
20737         };
20738         
20739         if(this.inputType != 'radio'){
20740             var hidden =  {
20741                 tag: 'input',
20742                 type : 'hidden',
20743                 cls : 'roo-hidden-value',
20744                 value : this.checked ? this.inputValue : this.valueOff
20745             };
20746         }
20747         
20748             
20749         if (this.weight) { // Validity check?
20750             cfg.cls += " " + this.inputType + "-" + this.weight;
20751         }
20752         
20753         if (this.disabled) {
20754             input.disabled=true;
20755         }
20756         
20757         if(this.checked){
20758             input.checked = this.checked;
20759         }
20760         
20761         if (this.name) {
20762             
20763             input.name = this.name;
20764             
20765             if(this.inputType != 'radio'){
20766                 hidden.name = this.name;
20767                 input.name = '_hidden_' + this.name;
20768             }
20769         }
20770         
20771         if (this.size) {
20772             input.cls += ' input-' + this.size;
20773         }
20774         
20775         var settings=this;
20776         
20777         ['xs','sm','md','lg'].map(function(size){
20778             if (settings[size]) {
20779                 cfg.cls += ' col-' + size + '-' + settings[size];
20780             }
20781         });
20782         
20783         var inputblock = input;
20784          
20785         if (this.before || this.after) {
20786             
20787             inputblock = {
20788                 cls : 'input-group',
20789                 cn :  [] 
20790             };
20791             
20792             if (this.before) {
20793                 inputblock.cn.push({
20794                     tag :'span',
20795                     cls : 'input-group-addon',
20796                     html : this.before
20797                 });
20798             }
20799             
20800             inputblock.cn.push(input);
20801             
20802             if(this.inputType != 'radio'){
20803                 inputblock.cn.push(hidden);
20804             }
20805             
20806             if (this.after) {
20807                 inputblock.cn.push({
20808                     tag :'span',
20809                     cls : 'input-group-addon',
20810                     html : this.after
20811                 });
20812             }
20813             
20814         }
20815         
20816         if (align ==='left' && this.fieldLabel.length) {
20817 //                Roo.log("left and has label");
20818             cfg.cn = [
20819                 {
20820                     tag: 'label',
20821                     'for' :  id,
20822                     cls : 'control-label',
20823                     html : this.fieldLabel
20824                 },
20825                 {
20826                     cls : "", 
20827                     cn: [
20828                         inputblock
20829                     ]
20830                 }
20831             ];
20832             
20833             if(this.labelWidth > 12){
20834                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20835             }
20836             
20837             if(this.labelWidth < 13 && this.labelmd == 0){
20838                 this.labelmd = this.labelWidth;
20839             }
20840             
20841             if(this.labellg > 0){
20842                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20843                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20844             }
20845             
20846             if(this.labelmd > 0){
20847                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20848                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20849             }
20850             
20851             if(this.labelsm > 0){
20852                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20853                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20854             }
20855             
20856             if(this.labelxs > 0){
20857                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20858                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20859             }
20860             
20861         } else if ( this.fieldLabel.length) {
20862 //                Roo.log(" label");
20863                 cfg.cn = [
20864                    
20865                     {
20866                         tag: this.boxLabel ? 'span' : 'label',
20867                         'for': id,
20868                         cls: 'control-label box-input-label',
20869                         //cls : 'input-group-addon',
20870                         html : this.fieldLabel
20871                     },
20872                     
20873                     inputblock
20874                     
20875                 ];
20876
20877         } else {
20878             
20879 //                Roo.log(" no label && no align");
20880                 cfg.cn = [  inputblock ] ;
20881                 
20882                 
20883         }
20884         
20885         if(this.boxLabel){
20886              var boxLabelCfg = {
20887                 tag: 'label',
20888                 //'for': id, // box label is handled by onclick - so no for...
20889                 cls: 'box-label',
20890                 html: this.boxLabel
20891             };
20892             
20893             if(this.tooltip){
20894                 boxLabelCfg.tooltip = this.tooltip;
20895             }
20896              
20897             cfg.cn.push(boxLabelCfg);
20898         }
20899         
20900         if(this.inputType != 'radio'){
20901             cfg.cn.push(hidden);
20902         }
20903         
20904         return cfg;
20905         
20906     },
20907     
20908     /**
20909      * return the real input element.
20910      */
20911     inputEl: function ()
20912     {
20913         return this.el.select('input.roo-' + this.inputType,true).first();
20914     },
20915     hiddenEl: function ()
20916     {
20917         return this.el.select('input.roo-hidden-value',true).first();
20918     },
20919     
20920     labelEl: function()
20921     {
20922         return this.el.select('label.control-label',true).first();
20923     },
20924     /* depricated... */
20925     
20926     label: function()
20927     {
20928         return this.labelEl();
20929     },
20930     
20931     boxLabelEl: function()
20932     {
20933         return this.el.select('label.box-label',true).first();
20934     },
20935     
20936     initEvents : function()
20937     {
20938 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20939         
20940         this.inputEl().on('click', this.onClick,  this);
20941         
20942         if (this.boxLabel) { 
20943             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20944         }
20945         
20946         this.startValue = this.getValue();
20947         
20948         if(this.groupId){
20949             Roo.bootstrap.CheckBox.register(this);
20950         }
20951     },
20952     
20953     onClick : function(e)
20954     {   
20955         if(this.fireEvent('click', this, e) !== false){
20956             this.setChecked(!this.checked);
20957         }
20958         
20959     },
20960     
20961     setChecked : function(state,suppressEvent)
20962     {
20963         this.startValue = this.getValue();
20964
20965         if(this.inputType == 'radio'){
20966             
20967             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20968                 e.dom.checked = false;
20969             });
20970             
20971             this.inputEl().dom.checked = true;
20972             
20973             this.inputEl().dom.value = this.inputValue;
20974             
20975             if(suppressEvent !== true){
20976                 this.fireEvent('check', this, true);
20977             }
20978             
20979             this.validate();
20980             
20981             return;
20982         }
20983         
20984         this.checked = state;
20985         
20986         this.inputEl().dom.checked = state;
20987         
20988         
20989         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20990         
20991         if(suppressEvent !== true){
20992             this.fireEvent('check', this, state);
20993         }
20994         
20995         this.validate();
20996     },
20997     
20998     getValue : function()
20999     {
21000         if(this.inputType == 'radio'){
21001             return this.getGroupValue();
21002         }
21003         
21004         return this.hiddenEl().dom.value;
21005         
21006     },
21007     
21008     getGroupValue : function()
21009     {
21010         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21011             return '';
21012         }
21013         
21014         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21015     },
21016     
21017     setValue : function(v,suppressEvent)
21018     {
21019         if(this.inputType == 'radio'){
21020             this.setGroupValue(v, suppressEvent);
21021             return;
21022         }
21023         
21024         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21025         
21026         this.validate();
21027     },
21028     
21029     setGroupValue : function(v, suppressEvent)
21030     {
21031         this.startValue = this.getValue();
21032         
21033         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21034             e.dom.checked = false;
21035             
21036             if(e.dom.value == v){
21037                 e.dom.checked = true;
21038             }
21039         });
21040         
21041         if(suppressEvent !== true){
21042             this.fireEvent('check', this, true);
21043         }
21044
21045         this.validate();
21046         
21047         return;
21048     },
21049     
21050     validate : function()
21051     {
21052         if(this.getVisibilityEl().hasClass('hidden')){
21053             return true;
21054         }
21055         
21056         if(
21057                 this.disabled || 
21058                 (this.inputType == 'radio' && this.validateRadio()) ||
21059                 (this.inputType == 'checkbox' && this.validateCheckbox())
21060         ){
21061             this.markValid();
21062             return true;
21063         }
21064         
21065         this.markInvalid();
21066         return false;
21067     },
21068     
21069     validateRadio : function()
21070     {
21071         if(this.getVisibilityEl().hasClass('hidden')){
21072             return true;
21073         }
21074         
21075         if(this.allowBlank){
21076             return true;
21077         }
21078         
21079         var valid = false;
21080         
21081         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21082             if(!e.dom.checked){
21083                 return;
21084             }
21085             
21086             valid = true;
21087             
21088             return false;
21089         });
21090         
21091         return valid;
21092     },
21093     
21094     validateCheckbox : function()
21095     {
21096         if(!this.groupId){
21097             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21098             //return (this.getValue() == this.inputValue) ? true : false;
21099         }
21100         
21101         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21102         
21103         if(!group){
21104             return false;
21105         }
21106         
21107         var r = false;
21108         
21109         for(var i in group){
21110             if(group[i].el.isVisible(true)){
21111                 r = false;
21112                 break;
21113             }
21114             
21115             r = true;
21116         }
21117         
21118         for(var i in group){
21119             if(r){
21120                 break;
21121             }
21122             
21123             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21124         }
21125         
21126         return r;
21127     },
21128     
21129     /**
21130      * Mark this field as valid
21131      */
21132     markValid : function()
21133     {
21134         var _this = this;
21135         
21136         this.fireEvent('valid', this);
21137         
21138         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21139         
21140         if(this.groupId){
21141             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21142         }
21143         
21144         if(label){
21145             label.markValid();
21146         }
21147
21148         if(this.inputType == 'radio'){
21149             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21150                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21151                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21152             });
21153             
21154             return;
21155         }
21156
21157         if(!this.groupId){
21158             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21159             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21160             return;
21161         }
21162         
21163         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21164         
21165         if(!group){
21166             return;
21167         }
21168         
21169         for(var i in group){
21170             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21171             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21172         }
21173     },
21174     
21175      /**
21176      * Mark this field as invalid
21177      * @param {String} msg The validation message
21178      */
21179     markInvalid : function(msg)
21180     {
21181         if(this.allowBlank){
21182             return;
21183         }
21184         
21185         var _this = this;
21186         
21187         this.fireEvent('invalid', this, msg);
21188         
21189         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21190         
21191         if(this.groupId){
21192             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21193         }
21194         
21195         if(label){
21196             label.markInvalid();
21197         }
21198             
21199         if(this.inputType == 'radio'){
21200             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21201                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21202                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21203             });
21204             
21205             return;
21206         }
21207         
21208         if(!this.groupId){
21209             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21210             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21211             return;
21212         }
21213         
21214         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21215         
21216         if(!group){
21217             return;
21218         }
21219         
21220         for(var i in group){
21221             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21222             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21223         }
21224         
21225     },
21226     
21227     clearInvalid : function()
21228     {
21229         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21230         
21231         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21232         
21233         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21234         
21235         if (label && label.iconEl) {
21236             label.iconEl.removeClass(label.validClass);
21237             label.iconEl.removeClass(label.invalidClass);
21238         }
21239     },
21240     
21241     disable : function()
21242     {
21243         if(this.inputType != 'radio'){
21244             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21245             return;
21246         }
21247         
21248         var _this = this;
21249         
21250         if(this.rendered){
21251             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21252                 _this.getActionEl().addClass(this.disabledClass);
21253                 e.dom.disabled = true;
21254             });
21255         }
21256         
21257         this.disabled = true;
21258         this.fireEvent("disable", this);
21259         return this;
21260     },
21261
21262     enable : function()
21263     {
21264         if(this.inputType != 'radio'){
21265             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21266             return;
21267         }
21268         
21269         var _this = this;
21270         
21271         if(this.rendered){
21272             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21273                 _this.getActionEl().removeClass(this.disabledClass);
21274                 e.dom.disabled = false;
21275             });
21276         }
21277         
21278         this.disabled = false;
21279         this.fireEvent("enable", this);
21280         return this;
21281     },
21282     
21283     setBoxLabel : function(v)
21284     {
21285         this.boxLabel = v;
21286         
21287         if(this.rendered){
21288             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21289         }
21290     }
21291
21292 });
21293
21294 Roo.apply(Roo.bootstrap.CheckBox, {
21295     
21296     groups: {},
21297     
21298      /**
21299     * register a CheckBox Group
21300     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21301     */
21302     register : function(checkbox)
21303     {
21304         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21305             this.groups[checkbox.groupId] = {};
21306         }
21307         
21308         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21309             return;
21310         }
21311         
21312         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21313         
21314     },
21315     /**
21316     * fetch a CheckBox Group based on the group ID
21317     * @param {string} the group ID
21318     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21319     */
21320     get: function(groupId) {
21321         if (typeof(this.groups[groupId]) == 'undefined') {
21322             return false;
21323         }
21324         
21325         return this.groups[groupId] ;
21326     }
21327     
21328     
21329 });
21330 /*
21331  * - LGPL
21332  *
21333  * RadioItem
21334  * 
21335  */
21336
21337 /**
21338  * @class Roo.bootstrap.Radio
21339  * @extends Roo.bootstrap.Component
21340  * Bootstrap Radio class
21341  * @cfg {String} boxLabel - the label associated
21342  * @cfg {String} value - the value of radio
21343  * 
21344  * @constructor
21345  * Create a new Radio
21346  * @param {Object} config The config object
21347  */
21348 Roo.bootstrap.Radio = function(config){
21349     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21350     
21351 };
21352
21353 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21354     
21355     boxLabel : '',
21356     
21357     value : '',
21358     
21359     getAutoCreate : function()
21360     {
21361         var cfg = {
21362             tag : 'div',
21363             cls : 'form-group radio',
21364             cn : [
21365                 {
21366                     tag : 'label',
21367                     cls : 'box-label',
21368                     html : this.boxLabel
21369                 }
21370             ]
21371         };
21372         
21373         return cfg;
21374     },
21375     
21376     initEvents : function() 
21377     {
21378         this.parent().register(this);
21379         
21380         this.el.on('click', this.onClick, this);
21381         
21382     },
21383     
21384     onClick : function(e)
21385     {
21386         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21387             this.setChecked(true);
21388         }
21389     },
21390     
21391     setChecked : function(state, suppressEvent)
21392     {
21393         this.parent().setValue(this.value, suppressEvent);
21394         
21395     },
21396     
21397     setBoxLabel : function(v)
21398     {
21399         this.boxLabel = v;
21400         
21401         if(this.rendered){
21402             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21403         }
21404     }
21405     
21406 });
21407  
21408
21409  /*
21410  * - LGPL
21411  *
21412  * Input
21413  * 
21414  */
21415
21416 /**
21417  * @class Roo.bootstrap.SecurePass
21418  * @extends Roo.bootstrap.Input
21419  * Bootstrap SecurePass class
21420  *
21421  * 
21422  * @constructor
21423  * Create a new SecurePass
21424  * @param {Object} config The config object
21425  */
21426  
21427 Roo.bootstrap.SecurePass = function (config) {
21428     // these go here, so the translation tool can replace them..
21429     this.errors = {
21430         PwdEmpty: "Please type a password, and then retype it to confirm.",
21431         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21432         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21433         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21434         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21435         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21436         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21437         TooWeak: "Your password is Too Weak."
21438     },
21439     this.meterLabel = "Password strength:";
21440     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21441     this.meterClass = [
21442         "roo-password-meter-tooweak", 
21443         "roo-password-meter-weak", 
21444         "roo-password-meter-medium", 
21445         "roo-password-meter-strong", 
21446         "roo-password-meter-grey"
21447     ];
21448     
21449     this.errors = {};
21450     
21451     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21452 }
21453
21454 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21455     /**
21456      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21457      * {
21458      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21459      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21460      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21461      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21462      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21463      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21464      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21465      * })
21466      */
21467     // private
21468     
21469     meterWidth: 300,
21470     errorMsg :'',    
21471     errors: false,
21472     imageRoot: '/',
21473     /**
21474      * @cfg {String/Object} Label for the strength meter (defaults to
21475      * 'Password strength:')
21476      */
21477     // private
21478     meterLabel: '',
21479     /**
21480      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21481      * ['Weak', 'Medium', 'Strong'])
21482      */
21483     // private    
21484     pwdStrengths: false,    
21485     // private
21486     strength: 0,
21487     // private
21488     _lastPwd: null,
21489     // private
21490     kCapitalLetter: 0,
21491     kSmallLetter: 1,
21492     kDigit: 2,
21493     kPunctuation: 3,
21494     
21495     insecure: false,
21496     // private
21497     initEvents: function ()
21498     {
21499         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21500
21501         if (this.el.is('input[type=password]') && Roo.isSafari) {
21502             this.el.on('keydown', this.SafariOnKeyDown, this);
21503         }
21504
21505         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21506     },
21507     // private
21508     onRender: function (ct, position)
21509     {
21510         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21511         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21512         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21513
21514         this.trigger.createChild({
21515                    cn: [
21516                     {
21517                     //id: 'PwdMeter',
21518                     tag: 'div',
21519                     cls: 'roo-password-meter-grey col-xs-12',
21520                     style: {
21521                         //width: 0,
21522                         //width: this.meterWidth + 'px'                                                
21523                         }
21524                     },
21525                     {                            
21526                          cls: 'roo-password-meter-text'                          
21527                     }
21528                 ]            
21529         });
21530
21531          
21532         if (this.hideTrigger) {
21533             this.trigger.setDisplayed(false);
21534         }
21535         this.setSize(this.width || '', this.height || '');
21536     },
21537     // private
21538     onDestroy: function ()
21539     {
21540         if (this.trigger) {
21541             this.trigger.removeAllListeners();
21542             this.trigger.remove();
21543         }
21544         if (this.wrap) {
21545             this.wrap.remove();
21546         }
21547         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21548     },
21549     // private
21550     checkStrength: function ()
21551     {
21552         var pwd = this.inputEl().getValue();
21553         if (pwd == this._lastPwd) {
21554             return;
21555         }
21556
21557         var strength;
21558         if (this.ClientSideStrongPassword(pwd)) {
21559             strength = 3;
21560         } else if (this.ClientSideMediumPassword(pwd)) {
21561             strength = 2;
21562         } else if (this.ClientSideWeakPassword(pwd)) {
21563             strength = 1;
21564         } else {
21565             strength = 0;
21566         }
21567         
21568         Roo.log('strength1: ' + strength);
21569         
21570         //var pm = this.trigger.child('div/div/div').dom;
21571         var pm = this.trigger.child('div/div');
21572         pm.removeClass(this.meterClass);
21573         pm.addClass(this.meterClass[strength]);
21574                 
21575         
21576         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21577                 
21578         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21579         
21580         this._lastPwd = pwd;
21581     },
21582     reset: function ()
21583     {
21584         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21585         
21586         this._lastPwd = '';
21587         
21588         var pm = this.trigger.child('div/div');
21589         pm.removeClass(this.meterClass);
21590         pm.addClass('roo-password-meter-grey');        
21591         
21592         
21593         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21594         
21595         pt.innerHTML = '';
21596         this.inputEl().dom.type='password';
21597     },
21598     // private
21599     validateValue: function (value)
21600     {
21601         
21602         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21603             return false;
21604         }
21605         if (value.length == 0) {
21606             if (this.allowBlank) {
21607                 this.clearInvalid();
21608                 return true;
21609             }
21610
21611             this.markInvalid(this.errors.PwdEmpty);
21612             this.errorMsg = this.errors.PwdEmpty;
21613             return false;
21614         }
21615         
21616         if(this.insecure){
21617             return true;
21618         }
21619         
21620         if ('[\x21-\x7e]*'.match(value)) {
21621             this.markInvalid(this.errors.PwdBadChar);
21622             this.errorMsg = this.errors.PwdBadChar;
21623             return false;
21624         }
21625         if (value.length < 6) {
21626             this.markInvalid(this.errors.PwdShort);
21627             this.errorMsg = this.errors.PwdShort;
21628             return false;
21629         }
21630         if (value.length > 16) {
21631             this.markInvalid(this.errors.PwdLong);
21632             this.errorMsg = this.errors.PwdLong;
21633             return false;
21634         }
21635         var strength;
21636         if (this.ClientSideStrongPassword(value)) {
21637             strength = 3;
21638         } else if (this.ClientSideMediumPassword(value)) {
21639             strength = 2;
21640         } else if (this.ClientSideWeakPassword(value)) {
21641             strength = 1;
21642         } else {
21643             strength = 0;
21644         }
21645
21646         
21647         if (strength < 2) {
21648             //this.markInvalid(this.errors.TooWeak);
21649             this.errorMsg = this.errors.TooWeak;
21650             //return false;
21651         }
21652         
21653         
21654         console.log('strength2: ' + strength);
21655         
21656         //var pm = this.trigger.child('div/div/div').dom;
21657         
21658         var pm = this.trigger.child('div/div');
21659         pm.removeClass(this.meterClass);
21660         pm.addClass(this.meterClass[strength]);
21661                 
21662         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21663                 
21664         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21665         
21666         this.errorMsg = ''; 
21667         return true;
21668     },
21669     // private
21670     CharacterSetChecks: function (type)
21671     {
21672         this.type = type;
21673         this.fResult = false;
21674     },
21675     // private
21676     isctype: function (character, type)
21677     {
21678         switch (type) {  
21679             case this.kCapitalLetter:
21680                 if (character >= 'A' && character <= 'Z') {
21681                     return true;
21682                 }
21683                 break;
21684             
21685             case this.kSmallLetter:
21686                 if (character >= 'a' && character <= 'z') {
21687                     return true;
21688                 }
21689                 break;
21690             
21691             case this.kDigit:
21692                 if (character >= '0' && character <= '9') {
21693                     return true;
21694                 }
21695                 break;
21696             
21697             case this.kPunctuation:
21698                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21699                     return true;
21700                 }
21701                 break;
21702             
21703             default:
21704                 return false;
21705         }
21706
21707     },
21708     // private
21709     IsLongEnough: function (pwd, size)
21710     {
21711         return !(pwd == null || isNaN(size) || pwd.length < size);
21712     },
21713     // private
21714     SpansEnoughCharacterSets: function (word, nb)
21715     {
21716         if (!this.IsLongEnough(word, nb))
21717         {
21718             return false;
21719         }
21720
21721         var characterSetChecks = new Array(
21722             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21723             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21724         );
21725         
21726         for (var index = 0; index < word.length; ++index) {
21727             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21728                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21729                     characterSetChecks[nCharSet].fResult = true;
21730                     break;
21731                 }
21732             }
21733         }
21734
21735         var nCharSets = 0;
21736         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21737             if (characterSetChecks[nCharSet].fResult) {
21738                 ++nCharSets;
21739             }
21740         }
21741
21742         if (nCharSets < nb) {
21743             return false;
21744         }
21745         return true;
21746     },
21747     // private
21748     ClientSideStrongPassword: function (pwd)
21749     {
21750         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21751     },
21752     // private
21753     ClientSideMediumPassword: function (pwd)
21754     {
21755         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21756     },
21757     // private
21758     ClientSideWeakPassword: function (pwd)
21759     {
21760         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21761     }
21762           
21763 })//<script type="text/javascript">
21764
21765 /*
21766  * Based  Ext JS Library 1.1.1
21767  * Copyright(c) 2006-2007, Ext JS, LLC.
21768  * LGPL
21769  *
21770  */
21771  
21772 /**
21773  * @class Roo.HtmlEditorCore
21774  * @extends Roo.Component
21775  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21776  *
21777  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21778  */
21779
21780 Roo.HtmlEditorCore = function(config){
21781     
21782     
21783     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21784     
21785     
21786     this.addEvents({
21787         /**
21788          * @event initialize
21789          * Fires when the editor is fully initialized (including the iframe)
21790          * @param {Roo.HtmlEditorCore} this
21791          */
21792         initialize: true,
21793         /**
21794          * @event activate
21795          * Fires when the editor is first receives the focus. Any insertion must wait
21796          * until after this event.
21797          * @param {Roo.HtmlEditorCore} this
21798          */
21799         activate: true,
21800          /**
21801          * @event beforesync
21802          * Fires before the textarea is updated with content from the editor iframe. Return false
21803          * to cancel the sync.
21804          * @param {Roo.HtmlEditorCore} this
21805          * @param {String} html
21806          */
21807         beforesync: true,
21808          /**
21809          * @event beforepush
21810          * Fires before the iframe editor is updated with content from the textarea. Return false
21811          * to cancel the push.
21812          * @param {Roo.HtmlEditorCore} this
21813          * @param {String} html
21814          */
21815         beforepush: true,
21816          /**
21817          * @event sync
21818          * Fires when the textarea is updated with content from the editor iframe.
21819          * @param {Roo.HtmlEditorCore} this
21820          * @param {String} html
21821          */
21822         sync: true,
21823          /**
21824          * @event push
21825          * Fires when the iframe editor is updated with content from the textarea.
21826          * @param {Roo.HtmlEditorCore} this
21827          * @param {String} html
21828          */
21829         push: true,
21830         
21831         /**
21832          * @event editorevent
21833          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21834          * @param {Roo.HtmlEditorCore} this
21835          */
21836         editorevent: true
21837         
21838     });
21839     
21840     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21841     
21842     // defaults : white / black...
21843     this.applyBlacklists();
21844     
21845     
21846     
21847 };
21848
21849
21850 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21851
21852
21853      /**
21854      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21855      */
21856     
21857     owner : false,
21858     
21859      /**
21860      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21861      *                        Roo.resizable.
21862      */
21863     resizable : false,
21864      /**
21865      * @cfg {Number} height (in pixels)
21866      */   
21867     height: 300,
21868    /**
21869      * @cfg {Number} width (in pixels)
21870      */   
21871     width: 500,
21872     
21873     /**
21874      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21875      * 
21876      */
21877     stylesheets: false,
21878     
21879     // id of frame..
21880     frameId: false,
21881     
21882     // private properties
21883     validationEvent : false,
21884     deferHeight: true,
21885     initialized : false,
21886     activated : false,
21887     sourceEditMode : false,
21888     onFocus : Roo.emptyFn,
21889     iframePad:3,
21890     hideMode:'offsets',
21891     
21892     clearUp: true,
21893     
21894     // blacklist + whitelisted elements..
21895     black: false,
21896     white: false,
21897      
21898     bodyCls : '',
21899
21900     /**
21901      * Protected method that will not generally be called directly. It
21902      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21903      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21904      */
21905     getDocMarkup : function(){
21906         // body styles..
21907         var st = '';
21908         
21909         // inherit styels from page...?? 
21910         if (this.stylesheets === false) {
21911             
21912             Roo.get(document.head).select('style').each(function(node) {
21913                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21914             });
21915             
21916             Roo.get(document.head).select('link').each(function(node) { 
21917                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21918             });
21919             
21920         } else if (!this.stylesheets.length) {
21921                 // simple..
21922                 st = '<style type="text/css">' +
21923                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21924                    '</style>';
21925         } else { 
21926             st = '<style type="text/css">' +
21927                     this.stylesheets +
21928                 '</style>';
21929         }
21930         
21931         st +=  '<style type="text/css">' +
21932             'IMG { cursor: pointer } ' +
21933         '</style>';
21934
21935         var cls = 'roo-htmleditor-body';
21936         
21937         if(this.bodyCls.length){
21938             cls += ' ' + this.bodyCls;
21939         }
21940         
21941         return '<html><head>' + st  +
21942             //<style type="text/css">' +
21943             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21944             //'</style>' +
21945             ' </head><body class="' +  cls + '"></body></html>';
21946     },
21947
21948     // private
21949     onRender : function(ct, position)
21950     {
21951         var _t = this;
21952         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21953         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21954         
21955         
21956         this.el.dom.style.border = '0 none';
21957         this.el.dom.setAttribute('tabIndex', -1);
21958         this.el.addClass('x-hidden hide');
21959         
21960         
21961         
21962         if(Roo.isIE){ // fix IE 1px bogus margin
21963             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21964         }
21965        
21966         
21967         this.frameId = Roo.id();
21968         
21969          
21970         
21971         var iframe = this.owner.wrap.createChild({
21972             tag: 'iframe',
21973             cls: 'form-control', // bootstrap..
21974             id: this.frameId,
21975             name: this.frameId,
21976             frameBorder : 'no',
21977             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21978         }, this.el
21979         );
21980         
21981         
21982         this.iframe = iframe.dom;
21983
21984          this.assignDocWin();
21985         
21986         this.doc.designMode = 'on';
21987        
21988         this.doc.open();
21989         this.doc.write(this.getDocMarkup());
21990         this.doc.close();
21991
21992         
21993         var task = { // must defer to wait for browser to be ready
21994             run : function(){
21995                 //console.log("run task?" + this.doc.readyState);
21996                 this.assignDocWin();
21997                 if(this.doc.body || this.doc.readyState == 'complete'){
21998                     try {
21999                         this.doc.designMode="on";
22000                     } catch (e) {
22001                         return;
22002                     }
22003                     Roo.TaskMgr.stop(task);
22004                     this.initEditor.defer(10, this);
22005                 }
22006             },
22007             interval : 10,
22008             duration: 10000,
22009             scope: this
22010         };
22011         Roo.TaskMgr.start(task);
22012
22013     },
22014
22015     // private
22016     onResize : function(w, h)
22017     {
22018          Roo.log('resize: ' +w + ',' + h );
22019         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22020         if(!this.iframe){
22021             return;
22022         }
22023         if(typeof w == 'number'){
22024             
22025             this.iframe.style.width = w + 'px';
22026         }
22027         if(typeof h == 'number'){
22028             
22029             this.iframe.style.height = h + 'px';
22030             if(this.doc){
22031                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22032             }
22033         }
22034         
22035     },
22036
22037     /**
22038      * Toggles the editor between standard and source edit mode.
22039      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22040      */
22041     toggleSourceEdit : function(sourceEditMode){
22042         
22043         this.sourceEditMode = sourceEditMode === true;
22044         
22045         if(this.sourceEditMode){
22046  
22047             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22048             
22049         }else{
22050             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22051             //this.iframe.className = '';
22052             this.deferFocus();
22053         }
22054         //this.setSize(this.owner.wrap.getSize());
22055         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22056     },
22057
22058     
22059   
22060
22061     /**
22062      * Protected method that will not generally be called directly. If you need/want
22063      * custom HTML cleanup, this is the method you should override.
22064      * @param {String} html The HTML to be cleaned
22065      * return {String} The cleaned HTML
22066      */
22067     cleanHtml : function(html){
22068         html = String(html);
22069         if(html.length > 5){
22070             if(Roo.isSafari){ // strip safari nonsense
22071                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22072             }
22073         }
22074         if(html == '&nbsp;'){
22075             html = '';
22076         }
22077         return html;
22078     },
22079
22080     /**
22081      * HTML Editor -> Textarea
22082      * Protected method that will not generally be called directly. Syncs the contents
22083      * of the editor iframe with the textarea.
22084      */
22085     syncValue : function(){
22086         if(this.initialized){
22087             var bd = (this.doc.body || this.doc.documentElement);
22088             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22089             var html = bd.innerHTML;
22090             if(Roo.isSafari){
22091                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22092                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22093                 if(m && m[1]){
22094                     html = '<div style="'+m[0]+'">' + html + '</div>';
22095                 }
22096             }
22097             html = this.cleanHtml(html);
22098             // fix up the special chars.. normaly like back quotes in word...
22099             // however we do not want to do this with chinese..
22100             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22101                 var cc = b.charCodeAt();
22102                 if (
22103                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22104                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22105                     (cc >= 0xf900 && cc < 0xfb00 )
22106                 ) {
22107                         return b;
22108                 }
22109                 return "&#"+cc+";" 
22110             });
22111             if(this.owner.fireEvent('beforesync', this, html) !== false){
22112                 this.el.dom.value = html;
22113                 this.owner.fireEvent('sync', this, html);
22114             }
22115         }
22116     },
22117
22118     /**
22119      * Protected method that will not generally be called directly. Pushes the value of the textarea
22120      * into the iframe editor.
22121      */
22122     pushValue : function(){
22123         if(this.initialized){
22124             var v = this.el.dom.value.trim();
22125             
22126 //            if(v.length < 1){
22127 //                v = '&#160;';
22128 //            }
22129             
22130             if(this.owner.fireEvent('beforepush', this, v) !== false){
22131                 var d = (this.doc.body || this.doc.documentElement);
22132                 d.innerHTML = v;
22133                 this.cleanUpPaste();
22134                 this.el.dom.value = d.innerHTML;
22135                 this.owner.fireEvent('push', this, v);
22136             }
22137         }
22138     },
22139
22140     // private
22141     deferFocus : function(){
22142         this.focus.defer(10, this);
22143     },
22144
22145     // doc'ed in Field
22146     focus : function(){
22147         if(this.win && !this.sourceEditMode){
22148             this.win.focus();
22149         }else{
22150             this.el.focus();
22151         }
22152     },
22153     
22154     assignDocWin: function()
22155     {
22156         var iframe = this.iframe;
22157         
22158          if(Roo.isIE){
22159             this.doc = iframe.contentWindow.document;
22160             this.win = iframe.contentWindow;
22161         } else {
22162 //            if (!Roo.get(this.frameId)) {
22163 //                return;
22164 //            }
22165 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22166 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22167             
22168             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22169                 return;
22170             }
22171             
22172             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22173             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22174         }
22175     },
22176     
22177     // private
22178     initEditor : function(){
22179         //console.log("INIT EDITOR");
22180         this.assignDocWin();
22181         
22182         
22183         
22184         this.doc.designMode="on";
22185         this.doc.open();
22186         this.doc.write(this.getDocMarkup());
22187         this.doc.close();
22188         
22189         var dbody = (this.doc.body || this.doc.documentElement);
22190         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22191         // this copies styles from the containing element into thsi one..
22192         // not sure why we need all of this..
22193         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22194         
22195         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22196         //ss['background-attachment'] = 'fixed'; // w3c
22197         dbody.bgProperties = 'fixed'; // ie
22198         //Roo.DomHelper.applyStyles(dbody, ss);
22199         Roo.EventManager.on(this.doc, {
22200             //'mousedown': this.onEditorEvent,
22201             'mouseup': this.onEditorEvent,
22202             'dblclick': this.onEditorEvent,
22203             'click': this.onEditorEvent,
22204             'keyup': this.onEditorEvent,
22205             buffer:100,
22206             scope: this
22207         });
22208         if(Roo.isGecko){
22209             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22210         }
22211         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22212             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22213         }
22214         this.initialized = true;
22215
22216         this.owner.fireEvent('initialize', this);
22217         this.pushValue();
22218     },
22219
22220     // private
22221     onDestroy : function(){
22222         
22223         
22224         
22225         if(this.rendered){
22226             
22227             //for (var i =0; i < this.toolbars.length;i++) {
22228             //    // fixme - ask toolbars for heights?
22229             //    this.toolbars[i].onDestroy();
22230            // }
22231             
22232             //this.wrap.dom.innerHTML = '';
22233             //this.wrap.remove();
22234         }
22235     },
22236
22237     // private
22238     onFirstFocus : function(){
22239         
22240         this.assignDocWin();
22241         
22242         
22243         this.activated = true;
22244          
22245     
22246         if(Roo.isGecko){ // prevent silly gecko errors
22247             this.win.focus();
22248             var s = this.win.getSelection();
22249             if(!s.focusNode || s.focusNode.nodeType != 3){
22250                 var r = s.getRangeAt(0);
22251                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22252                 r.collapse(true);
22253                 this.deferFocus();
22254             }
22255             try{
22256                 this.execCmd('useCSS', true);
22257                 this.execCmd('styleWithCSS', false);
22258             }catch(e){}
22259         }
22260         this.owner.fireEvent('activate', this);
22261     },
22262
22263     // private
22264     adjustFont: function(btn){
22265         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22266         //if(Roo.isSafari){ // safari
22267         //    adjust *= 2;
22268        // }
22269         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22270         if(Roo.isSafari){ // safari
22271             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22272             v =  (v < 10) ? 10 : v;
22273             v =  (v > 48) ? 48 : v;
22274             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22275             
22276         }
22277         
22278         
22279         v = Math.max(1, v+adjust);
22280         
22281         this.execCmd('FontSize', v  );
22282     },
22283
22284     onEditorEvent : function(e)
22285     {
22286         this.owner.fireEvent('editorevent', this, e);
22287       //  this.updateToolbar();
22288         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22289     },
22290
22291     insertTag : function(tg)
22292     {
22293         // could be a bit smarter... -> wrap the current selected tRoo..
22294         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22295             
22296             range = this.createRange(this.getSelection());
22297             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22298             wrappingNode.appendChild(range.extractContents());
22299             range.insertNode(wrappingNode);
22300
22301             return;
22302             
22303             
22304             
22305         }
22306         this.execCmd("formatblock",   tg);
22307         
22308     },
22309     
22310     insertText : function(txt)
22311     {
22312         
22313         
22314         var range = this.createRange();
22315         range.deleteContents();
22316                //alert(Sender.getAttribute('label'));
22317                
22318         range.insertNode(this.doc.createTextNode(txt));
22319     } ,
22320     
22321      
22322
22323     /**
22324      * Executes a Midas editor command on the editor document and performs necessary focus and
22325      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22326      * @param {String} cmd The Midas command
22327      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22328      */
22329     relayCmd : function(cmd, value){
22330         this.win.focus();
22331         this.execCmd(cmd, value);
22332         this.owner.fireEvent('editorevent', this);
22333         //this.updateToolbar();
22334         this.owner.deferFocus();
22335     },
22336
22337     /**
22338      * Executes a Midas editor command directly on the editor document.
22339      * For visual commands, you should use {@link #relayCmd} instead.
22340      * <b>This should only be called after the editor is initialized.</b>
22341      * @param {String} cmd The Midas command
22342      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22343      */
22344     execCmd : function(cmd, value){
22345         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22346         this.syncValue();
22347     },
22348  
22349  
22350    
22351     /**
22352      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22353      * to insert tRoo.
22354      * @param {String} text | dom node.. 
22355      */
22356     insertAtCursor : function(text)
22357     {
22358         
22359         if(!this.activated){
22360             return;
22361         }
22362         /*
22363         if(Roo.isIE){
22364             this.win.focus();
22365             var r = this.doc.selection.createRange();
22366             if(r){
22367                 r.collapse(true);
22368                 r.pasteHTML(text);
22369                 this.syncValue();
22370                 this.deferFocus();
22371             
22372             }
22373             return;
22374         }
22375         */
22376         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22377             this.win.focus();
22378             
22379             
22380             // from jquery ui (MIT licenced)
22381             var range, node;
22382             var win = this.win;
22383             
22384             if (win.getSelection && win.getSelection().getRangeAt) {
22385                 range = win.getSelection().getRangeAt(0);
22386                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22387                 range.insertNode(node);
22388             } else if (win.document.selection && win.document.selection.createRange) {
22389                 // no firefox support
22390                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22391                 win.document.selection.createRange().pasteHTML(txt);
22392             } else {
22393                 // no firefox support
22394                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22395                 this.execCmd('InsertHTML', txt);
22396             } 
22397             
22398             this.syncValue();
22399             
22400             this.deferFocus();
22401         }
22402     },
22403  // private
22404     mozKeyPress : function(e){
22405         if(e.ctrlKey){
22406             var c = e.getCharCode(), cmd;
22407           
22408             if(c > 0){
22409                 c = String.fromCharCode(c).toLowerCase();
22410                 switch(c){
22411                     case 'b':
22412                         cmd = 'bold';
22413                         break;
22414                     case 'i':
22415                         cmd = 'italic';
22416                         break;
22417                     
22418                     case 'u':
22419                         cmd = 'underline';
22420                         break;
22421                     
22422                     case 'v':
22423                         this.cleanUpPaste.defer(100, this);
22424                         return;
22425                         
22426                 }
22427                 if(cmd){
22428                     this.win.focus();
22429                     this.execCmd(cmd);
22430                     this.deferFocus();
22431                     e.preventDefault();
22432                 }
22433                 
22434             }
22435         }
22436     },
22437
22438     // private
22439     fixKeys : function(){ // load time branching for fastest keydown performance
22440         if(Roo.isIE){
22441             return function(e){
22442                 var k = e.getKey(), r;
22443                 if(k == e.TAB){
22444                     e.stopEvent();
22445                     r = this.doc.selection.createRange();
22446                     if(r){
22447                         r.collapse(true);
22448                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22449                         this.deferFocus();
22450                     }
22451                     return;
22452                 }
22453                 
22454                 if(k == e.ENTER){
22455                     r = this.doc.selection.createRange();
22456                     if(r){
22457                         var target = r.parentElement();
22458                         if(!target || target.tagName.toLowerCase() != 'li'){
22459                             e.stopEvent();
22460                             r.pasteHTML('<br />');
22461                             r.collapse(false);
22462                             r.select();
22463                         }
22464                     }
22465                 }
22466                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22467                     this.cleanUpPaste.defer(100, this);
22468                     return;
22469                 }
22470                 
22471                 
22472             };
22473         }else if(Roo.isOpera){
22474             return function(e){
22475                 var k = e.getKey();
22476                 if(k == e.TAB){
22477                     e.stopEvent();
22478                     this.win.focus();
22479                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22480                     this.deferFocus();
22481                 }
22482                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22483                     this.cleanUpPaste.defer(100, this);
22484                     return;
22485                 }
22486                 
22487             };
22488         }else if(Roo.isSafari){
22489             return function(e){
22490                 var k = e.getKey();
22491                 
22492                 if(k == e.TAB){
22493                     e.stopEvent();
22494                     this.execCmd('InsertText','\t');
22495                     this.deferFocus();
22496                     return;
22497                 }
22498                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22499                     this.cleanUpPaste.defer(100, this);
22500                     return;
22501                 }
22502                 
22503              };
22504         }
22505     }(),
22506     
22507     getAllAncestors: function()
22508     {
22509         var p = this.getSelectedNode();
22510         var a = [];
22511         if (!p) {
22512             a.push(p); // push blank onto stack..
22513             p = this.getParentElement();
22514         }
22515         
22516         
22517         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22518             a.push(p);
22519             p = p.parentNode;
22520         }
22521         a.push(this.doc.body);
22522         return a;
22523     },
22524     lastSel : false,
22525     lastSelNode : false,
22526     
22527     
22528     getSelection : function() 
22529     {
22530         this.assignDocWin();
22531         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22532     },
22533     
22534     getSelectedNode: function() 
22535     {
22536         // this may only work on Gecko!!!
22537         
22538         // should we cache this!!!!
22539         
22540         
22541         
22542          
22543         var range = this.createRange(this.getSelection()).cloneRange();
22544         
22545         if (Roo.isIE) {
22546             var parent = range.parentElement();
22547             while (true) {
22548                 var testRange = range.duplicate();
22549                 testRange.moveToElementText(parent);
22550                 if (testRange.inRange(range)) {
22551                     break;
22552                 }
22553                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22554                     break;
22555                 }
22556                 parent = parent.parentElement;
22557             }
22558             return parent;
22559         }
22560         
22561         // is ancestor a text element.
22562         var ac =  range.commonAncestorContainer;
22563         if (ac.nodeType == 3) {
22564             ac = ac.parentNode;
22565         }
22566         
22567         var ar = ac.childNodes;
22568          
22569         var nodes = [];
22570         var other_nodes = [];
22571         var has_other_nodes = false;
22572         for (var i=0;i<ar.length;i++) {
22573             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22574                 continue;
22575             }
22576             // fullly contained node.
22577             
22578             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22579                 nodes.push(ar[i]);
22580                 continue;
22581             }
22582             
22583             // probably selected..
22584             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22585                 other_nodes.push(ar[i]);
22586                 continue;
22587             }
22588             // outer..
22589             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22590                 continue;
22591             }
22592             
22593             
22594             has_other_nodes = true;
22595         }
22596         if (!nodes.length && other_nodes.length) {
22597             nodes= other_nodes;
22598         }
22599         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22600             return false;
22601         }
22602         
22603         return nodes[0];
22604     },
22605     createRange: function(sel)
22606     {
22607         // this has strange effects when using with 
22608         // top toolbar - not sure if it's a great idea.
22609         //this.editor.contentWindow.focus();
22610         if (typeof sel != "undefined") {
22611             try {
22612                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22613             } catch(e) {
22614                 return this.doc.createRange();
22615             }
22616         } else {
22617             return this.doc.createRange();
22618         }
22619     },
22620     getParentElement: function()
22621     {
22622         
22623         this.assignDocWin();
22624         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22625         
22626         var range = this.createRange(sel);
22627          
22628         try {
22629             var p = range.commonAncestorContainer;
22630             while (p.nodeType == 3) { // text node
22631                 p = p.parentNode;
22632             }
22633             return p;
22634         } catch (e) {
22635             return null;
22636         }
22637     
22638     },
22639     /***
22640      *
22641      * Range intersection.. the hard stuff...
22642      *  '-1' = before
22643      *  '0' = hits..
22644      *  '1' = after.
22645      *         [ -- selected range --- ]
22646      *   [fail]                        [fail]
22647      *
22648      *    basically..
22649      *      if end is before start or  hits it. fail.
22650      *      if start is after end or hits it fail.
22651      *
22652      *   if either hits (but other is outside. - then it's not 
22653      *   
22654      *    
22655      **/
22656     
22657     
22658     // @see http://www.thismuchiknow.co.uk/?p=64.
22659     rangeIntersectsNode : function(range, node)
22660     {
22661         var nodeRange = node.ownerDocument.createRange();
22662         try {
22663             nodeRange.selectNode(node);
22664         } catch (e) {
22665             nodeRange.selectNodeContents(node);
22666         }
22667     
22668         var rangeStartRange = range.cloneRange();
22669         rangeStartRange.collapse(true);
22670     
22671         var rangeEndRange = range.cloneRange();
22672         rangeEndRange.collapse(false);
22673     
22674         var nodeStartRange = nodeRange.cloneRange();
22675         nodeStartRange.collapse(true);
22676     
22677         var nodeEndRange = nodeRange.cloneRange();
22678         nodeEndRange.collapse(false);
22679     
22680         return rangeStartRange.compareBoundaryPoints(
22681                  Range.START_TO_START, nodeEndRange) == -1 &&
22682                rangeEndRange.compareBoundaryPoints(
22683                  Range.START_TO_START, nodeStartRange) == 1;
22684         
22685          
22686     },
22687     rangeCompareNode : function(range, node)
22688     {
22689         var nodeRange = node.ownerDocument.createRange();
22690         try {
22691             nodeRange.selectNode(node);
22692         } catch (e) {
22693             nodeRange.selectNodeContents(node);
22694         }
22695         
22696         
22697         range.collapse(true);
22698     
22699         nodeRange.collapse(true);
22700      
22701         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22702         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22703          
22704         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22705         
22706         var nodeIsBefore   =  ss == 1;
22707         var nodeIsAfter    = ee == -1;
22708         
22709         if (nodeIsBefore && nodeIsAfter) {
22710             return 0; // outer
22711         }
22712         if (!nodeIsBefore && nodeIsAfter) {
22713             return 1; //right trailed.
22714         }
22715         
22716         if (nodeIsBefore && !nodeIsAfter) {
22717             return 2;  // left trailed.
22718         }
22719         // fully contined.
22720         return 3;
22721     },
22722
22723     // private? - in a new class?
22724     cleanUpPaste :  function()
22725     {
22726         // cleans up the whole document..
22727         Roo.log('cleanuppaste');
22728         
22729         this.cleanUpChildren(this.doc.body);
22730         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22731         if (clean != this.doc.body.innerHTML) {
22732             this.doc.body.innerHTML = clean;
22733         }
22734         
22735     },
22736     
22737     cleanWordChars : function(input) {// change the chars to hex code
22738         var he = Roo.HtmlEditorCore;
22739         
22740         var output = input;
22741         Roo.each(he.swapCodes, function(sw) { 
22742             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22743             
22744             output = output.replace(swapper, sw[1]);
22745         });
22746         
22747         return output;
22748     },
22749     
22750     
22751     cleanUpChildren : function (n)
22752     {
22753         if (!n.childNodes.length) {
22754             return;
22755         }
22756         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22757            this.cleanUpChild(n.childNodes[i]);
22758         }
22759     },
22760     
22761     
22762         
22763     
22764     cleanUpChild : function (node)
22765     {
22766         var ed = this;
22767         //console.log(node);
22768         if (node.nodeName == "#text") {
22769             // clean up silly Windows -- stuff?
22770             return; 
22771         }
22772         if (node.nodeName == "#comment") {
22773             node.parentNode.removeChild(node);
22774             // clean up silly Windows -- stuff?
22775             return; 
22776         }
22777         var lcname = node.tagName.toLowerCase();
22778         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22779         // whitelist of tags..
22780         
22781         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22782             // remove node.
22783             node.parentNode.removeChild(node);
22784             return;
22785             
22786         }
22787         
22788         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22789         
22790         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22791         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22792         
22793         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22794         //    remove_keep_children = true;
22795         //}
22796         
22797         if (remove_keep_children) {
22798             this.cleanUpChildren(node);
22799             // inserts everything just before this node...
22800             while (node.childNodes.length) {
22801                 var cn = node.childNodes[0];
22802                 node.removeChild(cn);
22803                 node.parentNode.insertBefore(cn, node);
22804             }
22805             node.parentNode.removeChild(node);
22806             return;
22807         }
22808         
22809         if (!node.attributes || !node.attributes.length) {
22810             this.cleanUpChildren(node);
22811             return;
22812         }
22813         
22814         function cleanAttr(n,v)
22815         {
22816             
22817             if (v.match(/^\./) || v.match(/^\//)) {
22818                 return;
22819             }
22820             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22821                 return;
22822             }
22823             if (v.match(/^#/)) {
22824                 return;
22825             }
22826 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22827             node.removeAttribute(n);
22828             
22829         }
22830         
22831         var cwhite = this.cwhite;
22832         var cblack = this.cblack;
22833             
22834         function cleanStyle(n,v)
22835         {
22836             if (v.match(/expression/)) { //XSS?? should we even bother..
22837                 node.removeAttribute(n);
22838                 return;
22839             }
22840             
22841             var parts = v.split(/;/);
22842             var clean = [];
22843             
22844             Roo.each(parts, function(p) {
22845                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22846                 if (!p.length) {
22847                     return true;
22848                 }
22849                 var l = p.split(':').shift().replace(/\s+/g,'');
22850                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22851                 
22852                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22853 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22854                     //node.removeAttribute(n);
22855                     return true;
22856                 }
22857                 //Roo.log()
22858                 // only allow 'c whitelisted system attributes'
22859                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22860 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22861                     //node.removeAttribute(n);
22862                     return true;
22863                 }
22864                 
22865                 
22866                  
22867                 
22868                 clean.push(p);
22869                 return true;
22870             });
22871             if (clean.length) { 
22872                 node.setAttribute(n, clean.join(';'));
22873             } else {
22874                 node.removeAttribute(n);
22875             }
22876             
22877         }
22878         
22879         
22880         for (var i = node.attributes.length-1; i > -1 ; i--) {
22881             var a = node.attributes[i];
22882             //console.log(a);
22883             
22884             if (a.name.toLowerCase().substr(0,2)=='on')  {
22885                 node.removeAttribute(a.name);
22886                 continue;
22887             }
22888             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22889                 node.removeAttribute(a.name);
22890                 continue;
22891             }
22892             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22893                 cleanAttr(a.name,a.value); // fixme..
22894                 continue;
22895             }
22896             if (a.name == 'style') {
22897                 cleanStyle(a.name,a.value);
22898                 continue;
22899             }
22900             /// clean up MS crap..
22901             // tecnically this should be a list of valid class'es..
22902             
22903             
22904             if (a.name == 'class') {
22905                 if (a.value.match(/^Mso/)) {
22906                     node.className = '';
22907                 }
22908                 
22909                 if (a.value.match(/^body$/)) {
22910                     node.className = '';
22911                 }
22912                 continue;
22913             }
22914             
22915             // style cleanup!?
22916             // class cleanup?
22917             
22918         }
22919         
22920         
22921         this.cleanUpChildren(node);
22922         
22923         
22924     },
22925     
22926     /**
22927      * Clean up MS wordisms...
22928      */
22929     cleanWord : function(node)
22930     {
22931         
22932         
22933         if (!node) {
22934             this.cleanWord(this.doc.body);
22935             return;
22936         }
22937         if (node.nodeName == "#text") {
22938             // clean up silly Windows -- stuff?
22939             return; 
22940         }
22941         if (node.nodeName == "#comment") {
22942             node.parentNode.removeChild(node);
22943             // clean up silly Windows -- stuff?
22944             return; 
22945         }
22946         
22947         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22948             node.parentNode.removeChild(node);
22949             return;
22950         }
22951         
22952         // remove - but keep children..
22953         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22954             while (node.childNodes.length) {
22955                 var cn = node.childNodes[0];
22956                 node.removeChild(cn);
22957                 node.parentNode.insertBefore(cn, node);
22958             }
22959             node.parentNode.removeChild(node);
22960             this.iterateChildren(node, this.cleanWord);
22961             return;
22962         }
22963         // clean styles
22964         if (node.className.length) {
22965             
22966             var cn = node.className.split(/\W+/);
22967             var cna = [];
22968             Roo.each(cn, function(cls) {
22969                 if (cls.match(/Mso[a-zA-Z]+/)) {
22970                     return;
22971                 }
22972                 cna.push(cls);
22973             });
22974             node.className = cna.length ? cna.join(' ') : '';
22975             if (!cna.length) {
22976                 node.removeAttribute("class");
22977             }
22978         }
22979         
22980         if (node.hasAttribute("lang")) {
22981             node.removeAttribute("lang");
22982         }
22983         
22984         if (node.hasAttribute("style")) {
22985             
22986             var styles = node.getAttribute("style").split(";");
22987             var nstyle = [];
22988             Roo.each(styles, function(s) {
22989                 if (!s.match(/:/)) {
22990                     return;
22991                 }
22992                 var kv = s.split(":");
22993                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22994                     return;
22995                 }
22996                 // what ever is left... we allow.
22997                 nstyle.push(s);
22998             });
22999             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23000             if (!nstyle.length) {
23001                 node.removeAttribute('style');
23002             }
23003         }
23004         this.iterateChildren(node, this.cleanWord);
23005         
23006         
23007         
23008     },
23009     /**
23010      * iterateChildren of a Node, calling fn each time, using this as the scole..
23011      * @param {DomNode} node node to iterate children of.
23012      * @param {Function} fn method of this class to call on each item.
23013      */
23014     iterateChildren : function(node, fn)
23015     {
23016         if (!node.childNodes.length) {
23017                 return;
23018         }
23019         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23020            fn.call(this, node.childNodes[i])
23021         }
23022     },
23023     
23024     
23025     /**
23026      * cleanTableWidths.
23027      *
23028      * Quite often pasting from word etc.. results in tables with column and widths.
23029      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23030      *
23031      */
23032     cleanTableWidths : function(node)
23033     {
23034          
23035          
23036         if (!node) {
23037             this.cleanTableWidths(this.doc.body);
23038             return;
23039         }
23040         
23041         // ignore list...
23042         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23043             return; 
23044         }
23045         Roo.log(node.tagName);
23046         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23047             this.iterateChildren(node, this.cleanTableWidths);
23048             return;
23049         }
23050         if (node.hasAttribute('width')) {
23051             node.removeAttribute('width');
23052         }
23053         
23054          
23055         if (node.hasAttribute("style")) {
23056             // pretty basic...
23057             
23058             var styles = node.getAttribute("style").split(";");
23059             var nstyle = [];
23060             Roo.each(styles, function(s) {
23061                 if (!s.match(/:/)) {
23062                     return;
23063                 }
23064                 var kv = s.split(":");
23065                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23066                     return;
23067                 }
23068                 // what ever is left... we allow.
23069                 nstyle.push(s);
23070             });
23071             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23072             if (!nstyle.length) {
23073                 node.removeAttribute('style');
23074             }
23075         }
23076         
23077         this.iterateChildren(node, this.cleanTableWidths);
23078         
23079         
23080     },
23081     
23082     
23083     
23084     
23085     domToHTML : function(currentElement, depth, nopadtext) {
23086         
23087         depth = depth || 0;
23088         nopadtext = nopadtext || false;
23089     
23090         if (!currentElement) {
23091             return this.domToHTML(this.doc.body);
23092         }
23093         
23094         //Roo.log(currentElement);
23095         var j;
23096         var allText = false;
23097         var nodeName = currentElement.nodeName;
23098         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23099         
23100         if  (nodeName == '#text') {
23101             
23102             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23103         }
23104         
23105         
23106         var ret = '';
23107         if (nodeName != 'BODY') {
23108              
23109             var i = 0;
23110             // Prints the node tagName, such as <A>, <IMG>, etc
23111             if (tagName) {
23112                 var attr = [];
23113                 for(i = 0; i < currentElement.attributes.length;i++) {
23114                     // quoting?
23115                     var aname = currentElement.attributes.item(i).name;
23116                     if (!currentElement.attributes.item(i).value.length) {
23117                         continue;
23118                     }
23119                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23120                 }
23121                 
23122                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23123             } 
23124             else {
23125                 
23126                 // eack
23127             }
23128         } else {
23129             tagName = false;
23130         }
23131         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23132             return ret;
23133         }
23134         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23135             nopadtext = true;
23136         }
23137         
23138         
23139         // Traverse the tree
23140         i = 0;
23141         var currentElementChild = currentElement.childNodes.item(i);
23142         var allText = true;
23143         var innerHTML  = '';
23144         lastnode = '';
23145         while (currentElementChild) {
23146             // Formatting code (indent the tree so it looks nice on the screen)
23147             var nopad = nopadtext;
23148             if (lastnode == 'SPAN') {
23149                 nopad  = true;
23150             }
23151             // text
23152             if  (currentElementChild.nodeName == '#text') {
23153                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23154                 toadd = nopadtext ? toadd : toadd.trim();
23155                 if (!nopad && toadd.length > 80) {
23156                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23157                 }
23158                 innerHTML  += toadd;
23159                 
23160                 i++;
23161                 currentElementChild = currentElement.childNodes.item(i);
23162                 lastNode = '';
23163                 continue;
23164             }
23165             allText = false;
23166             
23167             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23168                 
23169             // Recursively traverse the tree structure of the child node
23170             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23171             lastnode = currentElementChild.nodeName;
23172             i++;
23173             currentElementChild=currentElement.childNodes.item(i);
23174         }
23175         
23176         ret += innerHTML;
23177         
23178         if (!allText) {
23179                 // The remaining code is mostly for formatting the tree
23180             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23181         }
23182         
23183         
23184         if (tagName) {
23185             ret+= "</"+tagName+">";
23186         }
23187         return ret;
23188         
23189     },
23190         
23191     applyBlacklists : function()
23192     {
23193         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23194         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23195         
23196         this.white = [];
23197         this.black = [];
23198         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23199             if (b.indexOf(tag) > -1) {
23200                 return;
23201             }
23202             this.white.push(tag);
23203             
23204         }, this);
23205         
23206         Roo.each(w, function(tag) {
23207             if (b.indexOf(tag) > -1) {
23208                 return;
23209             }
23210             if (this.white.indexOf(tag) > -1) {
23211                 return;
23212             }
23213             this.white.push(tag);
23214             
23215         }, this);
23216         
23217         
23218         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23219             if (w.indexOf(tag) > -1) {
23220                 return;
23221             }
23222             this.black.push(tag);
23223             
23224         }, this);
23225         
23226         Roo.each(b, function(tag) {
23227             if (w.indexOf(tag) > -1) {
23228                 return;
23229             }
23230             if (this.black.indexOf(tag) > -1) {
23231                 return;
23232             }
23233             this.black.push(tag);
23234             
23235         }, this);
23236         
23237         
23238         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23239         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23240         
23241         this.cwhite = [];
23242         this.cblack = [];
23243         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23244             if (b.indexOf(tag) > -1) {
23245                 return;
23246             }
23247             this.cwhite.push(tag);
23248             
23249         }, this);
23250         
23251         Roo.each(w, function(tag) {
23252             if (b.indexOf(tag) > -1) {
23253                 return;
23254             }
23255             if (this.cwhite.indexOf(tag) > -1) {
23256                 return;
23257             }
23258             this.cwhite.push(tag);
23259             
23260         }, this);
23261         
23262         
23263         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23264             if (w.indexOf(tag) > -1) {
23265                 return;
23266             }
23267             this.cblack.push(tag);
23268             
23269         }, this);
23270         
23271         Roo.each(b, function(tag) {
23272             if (w.indexOf(tag) > -1) {
23273                 return;
23274             }
23275             if (this.cblack.indexOf(tag) > -1) {
23276                 return;
23277             }
23278             this.cblack.push(tag);
23279             
23280         }, this);
23281     },
23282     
23283     setStylesheets : function(stylesheets)
23284     {
23285         if(typeof(stylesheets) == 'string'){
23286             Roo.get(this.iframe.contentDocument.head).createChild({
23287                 tag : 'link',
23288                 rel : 'stylesheet',
23289                 type : 'text/css',
23290                 href : stylesheets
23291             });
23292             
23293             return;
23294         }
23295         var _this = this;
23296      
23297         Roo.each(stylesheets, function(s) {
23298             if(!s.length){
23299                 return;
23300             }
23301             
23302             Roo.get(_this.iframe.contentDocument.head).createChild({
23303                 tag : 'link',
23304                 rel : 'stylesheet',
23305                 type : 'text/css',
23306                 href : s
23307             });
23308         });
23309
23310         
23311     },
23312     
23313     removeStylesheets : function()
23314     {
23315         var _this = this;
23316         
23317         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23318             s.remove();
23319         });
23320     },
23321     
23322     setStyle : function(style)
23323     {
23324         Roo.get(this.iframe.contentDocument.head).createChild({
23325             tag : 'style',
23326             type : 'text/css',
23327             html : style
23328         });
23329
23330         return;
23331     }
23332     
23333     // hide stuff that is not compatible
23334     /**
23335      * @event blur
23336      * @hide
23337      */
23338     /**
23339      * @event change
23340      * @hide
23341      */
23342     /**
23343      * @event focus
23344      * @hide
23345      */
23346     /**
23347      * @event specialkey
23348      * @hide
23349      */
23350     /**
23351      * @cfg {String} fieldClass @hide
23352      */
23353     /**
23354      * @cfg {String} focusClass @hide
23355      */
23356     /**
23357      * @cfg {String} autoCreate @hide
23358      */
23359     /**
23360      * @cfg {String} inputType @hide
23361      */
23362     /**
23363      * @cfg {String} invalidClass @hide
23364      */
23365     /**
23366      * @cfg {String} invalidText @hide
23367      */
23368     /**
23369      * @cfg {String} msgFx @hide
23370      */
23371     /**
23372      * @cfg {String} validateOnBlur @hide
23373      */
23374 });
23375
23376 Roo.HtmlEditorCore.white = [
23377         'area', 'br', 'img', 'input', 'hr', 'wbr',
23378         
23379        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23380        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23381        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23382        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23383        'table',   'ul',         'xmp', 
23384        
23385        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23386       'thead',   'tr', 
23387      
23388       'dir', 'menu', 'ol', 'ul', 'dl',
23389        
23390       'embed',  'object'
23391 ];
23392
23393
23394 Roo.HtmlEditorCore.black = [
23395     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23396         'applet', // 
23397         'base',   'basefont', 'bgsound', 'blink',  'body', 
23398         'frame',  'frameset', 'head',    'html',   'ilayer', 
23399         'iframe', 'layer',  'link',     'meta',    'object',   
23400         'script', 'style' ,'title',  'xml' // clean later..
23401 ];
23402 Roo.HtmlEditorCore.clean = [
23403     'script', 'style', 'title', 'xml'
23404 ];
23405 Roo.HtmlEditorCore.remove = [
23406     'font'
23407 ];
23408 // attributes..
23409
23410 Roo.HtmlEditorCore.ablack = [
23411     'on'
23412 ];
23413     
23414 Roo.HtmlEditorCore.aclean = [ 
23415     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23416 ];
23417
23418 // protocols..
23419 Roo.HtmlEditorCore.pwhite= [
23420         'http',  'https',  'mailto'
23421 ];
23422
23423 // white listed style attributes.
23424 Roo.HtmlEditorCore.cwhite= [
23425       //  'text-align', /// default is to allow most things..
23426       
23427          
23428 //        'font-size'//??
23429 ];
23430
23431 // black listed style attributes.
23432 Roo.HtmlEditorCore.cblack= [
23433       //  'font-size' -- this can be set by the project 
23434 ];
23435
23436
23437 Roo.HtmlEditorCore.swapCodes   =[ 
23438     [    8211, "--" ], 
23439     [    8212, "--" ], 
23440     [    8216,  "'" ],  
23441     [    8217, "'" ],  
23442     [    8220, '"' ],  
23443     [    8221, '"' ],  
23444     [    8226, "*" ],  
23445     [    8230, "..." ]
23446 ]; 
23447
23448     /*
23449  * - LGPL
23450  *
23451  * HtmlEditor
23452  * 
23453  */
23454
23455 /**
23456  * @class Roo.bootstrap.HtmlEditor
23457  * @extends Roo.bootstrap.TextArea
23458  * Bootstrap HtmlEditor class
23459
23460  * @constructor
23461  * Create a new HtmlEditor
23462  * @param {Object} config The config object
23463  */
23464
23465 Roo.bootstrap.HtmlEditor = function(config){
23466     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23467     if (!this.toolbars) {
23468         this.toolbars = [];
23469     }
23470     
23471     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23472     this.addEvents({
23473             /**
23474              * @event initialize
23475              * Fires when the editor is fully initialized (including the iframe)
23476              * @param {HtmlEditor} this
23477              */
23478             initialize: true,
23479             /**
23480              * @event activate
23481              * Fires when the editor is first receives the focus. Any insertion must wait
23482              * until after this event.
23483              * @param {HtmlEditor} this
23484              */
23485             activate: true,
23486              /**
23487              * @event beforesync
23488              * Fires before the textarea is updated with content from the editor iframe. Return false
23489              * to cancel the sync.
23490              * @param {HtmlEditor} this
23491              * @param {String} html
23492              */
23493             beforesync: true,
23494              /**
23495              * @event beforepush
23496              * Fires before the iframe editor is updated with content from the textarea. Return false
23497              * to cancel the push.
23498              * @param {HtmlEditor} this
23499              * @param {String} html
23500              */
23501             beforepush: true,
23502              /**
23503              * @event sync
23504              * Fires when the textarea is updated with content from the editor iframe.
23505              * @param {HtmlEditor} this
23506              * @param {String} html
23507              */
23508             sync: true,
23509              /**
23510              * @event push
23511              * Fires when the iframe editor is updated with content from the textarea.
23512              * @param {HtmlEditor} this
23513              * @param {String} html
23514              */
23515             push: true,
23516              /**
23517              * @event editmodechange
23518              * Fires when the editor switches edit modes
23519              * @param {HtmlEditor} this
23520              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23521              */
23522             editmodechange: true,
23523             /**
23524              * @event editorevent
23525              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23526              * @param {HtmlEditor} this
23527              */
23528             editorevent: true,
23529             /**
23530              * @event firstfocus
23531              * Fires when on first focus - needed by toolbars..
23532              * @param {HtmlEditor} this
23533              */
23534             firstfocus: true,
23535             /**
23536              * @event autosave
23537              * Auto save the htmlEditor value as a file into Events
23538              * @param {HtmlEditor} this
23539              */
23540             autosave: true,
23541             /**
23542              * @event savedpreview
23543              * preview the saved version of htmlEditor
23544              * @param {HtmlEditor} this
23545              */
23546             savedpreview: true
23547         });
23548 };
23549
23550
23551 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23552     
23553     
23554       /**
23555      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23556      */
23557     toolbars : false,
23558     
23559      /**
23560     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23561     */
23562     btns : [],
23563    
23564      /**
23565      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23566      *                        Roo.resizable.
23567      */
23568     resizable : false,
23569      /**
23570      * @cfg {Number} height (in pixels)
23571      */   
23572     height: 300,
23573    /**
23574      * @cfg {Number} width (in pixels)
23575      */   
23576     width: false,
23577     
23578     /**
23579      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23580      * 
23581      */
23582     stylesheets: false,
23583     
23584     // id of frame..
23585     frameId: false,
23586     
23587     // private properties
23588     validationEvent : false,
23589     deferHeight: true,
23590     initialized : false,
23591     activated : false,
23592     
23593     onFocus : Roo.emptyFn,
23594     iframePad:3,
23595     hideMode:'offsets',
23596     
23597     tbContainer : false,
23598     
23599     bodyCls : '',
23600     
23601     toolbarContainer :function() {
23602         return this.wrap.select('.x-html-editor-tb',true).first();
23603     },
23604
23605     /**
23606      * Protected method that will not generally be called directly. It
23607      * is called when the editor creates its toolbar. Override this method if you need to
23608      * add custom toolbar buttons.
23609      * @param {HtmlEditor} editor
23610      */
23611     createToolbar : function(){
23612         Roo.log('renewing');
23613         Roo.log("create toolbars");
23614         
23615         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23616         this.toolbars[0].render(this.toolbarContainer());
23617         
23618         return;
23619         
23620 //        if (!editor.toolbars || !editor.toolbars.length) {
23621 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23622 //        }
23623 //        
23624 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23625 //            editor.toolbars[i] = Roo.factory(
23626 //                    typeof(editor.toolbars[i]) == 'string' ?
23627 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23628 //                Roo.bootstrap.HtmlEditor);
23629 //            editor.toolbars[i].init(editor);
23630 //        }
23631     },
23632
23633      
23634     // private
23635     onRender : function(ct, position)
23636     {
23637        // Roo.log("Call onRender: " + this.xtype);
23638         var _t = this;
23639         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23640       
23641         this.wrap = this.inputEl().wrap({
23642             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23643         });
23644         
23645         this.editorcore.onRender(ct, position);
23646          
23647         if (this.resizable) {
23648             this.resizeEl = new Roo.Resizable(this.wrap, {
23649                 pinned : true,
23650                 wrap: true,
23651                 dynamic : true,
23652                 minHeight : this.height,
23653                 height: this.height,
23654                 handles : this.resizable,
23655                 width: this.width,
23656                 listeners : {
23657                     resize : function(r, w, h) {
23658                         _t.onResize(w,h); // -something
23659                     }
23660                 }
23661             });
23662             
23663         }
23664         this.createToolbar(this);
23665        
23666         
23667         if(!this.width && this.resizable){
23668             this.setSize(this.wrap.getSize());
23669         }
23670         if (this.resizeEl) {
23671             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23672             // should trigger onReize..
23673         }
23674         
23675     },
23676
23677     // private
23678     onResize : function(w, h)
23679     {
23680         Roo.log('resize: ' +w + ',' + h );
23681         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23682         var ew = false;
23683         var eh = false;
23684         
23685         if(this.inputEl() ){
23686             if(typeof w == 'number'){
23687                 var aw = w - this.wrap.getFrameWidth('lr');
23688                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23689                 ew = aw;
23690             }
23691             if(typeof h == 'number'){
23692                  var tbh = -11;  // fixme it needs to tool bar size!
23693                 for (var i =0; i < this.toolbars.length;i++) {
23694                     // fixme - ask toolbars for heights?
23695                     tbh += this.toolbars[i].el.getHeight();
23696                     //if (this.toolbars[i].footer) {
23697                     //    tbh += this.toolbars[i].footer.el.getHeight();
23698                     //}
23699                 }
23700               
23701                 
23702                 
23703                 
23704                 
23705                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23706                 ah -= 5; // knock a few pixes off for look..
23707                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23708                 var eh = ah;
23709             }
23710         }
23711         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23712         this.editorcore.onResize(ew,eh);
23713         
23714     },
23715
23716     /**
23717      * Toggles the editor between standard and source edit mode.
23718      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23719      */
23720     toggleSourceEdit : function(sourceEditMode)
23721     {
23722         this.editorcore.toggleSourceEdit(sourceEditMode);
23723         
23724         if(this.editorcore.sourceEditMode){
23725             Roo.log('editor - showing textarea');
23726             
23727 //            Roo.log('in');
23728 //            Roo.log(this.syncValue());
23729             this.syncValue();
23730             this.inputEl().removeClass(['hide', 'x-hidden']);
23731             this.inputEl().dom.removeAttribute('tabIndex');
23732             this.inputEl().focus();
23733         }else{
23734             Roo.log('editor - hiding textarea');
23735 //            Roo.log('out')
23736 //            Roo.log(this.pushValue()); 
23737             this.pushValue();
23738             
23739             this.inputEl().addClass(['hide', 'x-hidden']);
23740             this.inputEl().dom.setAttribute('tabIndex', -1);
23741             //this.deferFocus();
23742         }
23743          
23744         if(this.resizable){
23745             this.setSize(this.wrap.getSize());
23746         }
23747         
23748         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23749     },
23750  
23751     // private (for BoxComponent)
23752     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23753
23754     // private (for BoxComponent)
23755     getResizeEl : function(){
23756         return this.wrap;
23757     },
23758
23759     // private (for BoxComponent)
23760     getPositionEl : function(){
23761         return this.wrap;
23762     },
23763
23764     // private
23765     initEvents : function(){
23766         this.originalValue = this.getValue();
23767     },
23768
23769 //    /**
23770 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23771 //     * @method
23772 //     */
23773 //    markInvalid : Roo.emptyFn,
23774 //    /**
23775 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23776 //     * @method
23777 //     */
23778 //    clearInvalid : Roo.emptyFn,
23779
23780     setValue : function(v){
23781         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23782         this.editorcore.pushValue();
23783     },
23784
23785      
23786     // private
23787     deferFocus : function(){
23788         this.focus.defer(10, this);
23789     },
23790
23791     // doc'ed in Field
23792     focus : function(){
23793         this.editorcore.focus();
23794         
23795     },
23796       
23797
23798     // private
23799     onDestroy : function(){
23800         
23801         
23802         
23803         if(this.rendered){
23804             
23805             for (var i =0; i < this.toolbars.length;i++) {
23806                 // fixme - ask toolbars for heights?
23807                 this.toolbars[i].onDestroy();
23808             }
23809             
23810             this.wrap.dom.innerHTML = '';
23811             this.wrap.remove();
23812         }
23813     },
23814
23815     // private
23816     onFirstFocus : function(){
23817         //Roo.log("onFirstFocus");
23818         this.editorcore.onFirstFocus();
23819          for (var i =0; i < this.toolbars.length;i++) {
23820             this.toolbars[i].onFirstFocus();
23821         }
23822         
23823     },
23824     
23825     // private
23826     syncValue : function()
23827     {   
23828         this.editorcore.syncValue();
23829     },
23830     
23831     pushValue : function()
23832     {   
23833         this.editorcore.pushValue();
23834     }
23835      
23836     
23837     // hide stuff that is not compatible
23838     /**
23839      * @event blur
23840      * @hide
23841      */
23842     /**
23843      * @event change
23844      * @hide
23845      */
23846     /**
23847      * @event focus
23848      * @hide
23849      */
23850     /**
23851      * @event specialkey
23852      * @hide
23853      */
23854     /**
23855      * @cfg {String} fieldClass @hide
23856      */
23857     /**
23858      * @cfg {String} focusClass @hide
23859      */
23860     /**
23861      * @cfg {String} autoCreate @hide
23862      */
23863     /**
23864      * @cfg {String} inputType @hide
23865      */
23866     /**
23867      * @cfg {String} invalidClass @hide
23868      */
23869     /**
23870      * @cfg {String} invalidText @hide
23871      */
23872     /**
23873      * @cfg {String} msgFx @hide
23874      */
23875     /**
23876      * @cfg {String} validateOnBlur @hide
23877      */
23878 });
23879  
23880     
23881    
23882    
23883    
23884       
23885 Roo.namespace('Roo.bootstrap.htmleditor');
23886 /**
23887  * @class Roo.bootstrap.HtmlEditorToolbar1
23888  * Basic Toolbar
23889  * 
23890  * Usage:
23891  *
23892  new Roo.bootstrap.HtmlEditor({
23893     ....
23894     toolbars : [
23895         new Roo.bootstrap.HtmlEditorToolbar1({
23896             disable : { fonts: 1 , format: 1, ..., ... , ...],
23897             btns : [ .... ]
23898         })
23899     }
23900      
23901  * 
23902  * @cfg {Object} disable List of elements to disable..
23903  * @cfg {Array} btns List of additional buttons.
23904  * 
23905  * 
23906  * NEEDS Extra CSS? 
23907  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23908  */
23909  
23910 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23911 {
23912     
23913     Roo.apply(this, config);
23914     
23915     // default disabled, based on 'good practice'..
23916     this.disable = this.disable || {};
23917     Roo.applyIf(this.disable, {
23918         fontSize : true,
23919         colors : true,
23920         specialElements : true
23921     });
23922     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23923     
23924     this.editor = config.editor;
23925     this.editorcore = config.editor.editorcore;
23926     
23927     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23928     
23929     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23930     // dont call parent... till later.
23931 }
23932 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23933      
23934     bar : true,
23935     
23936     editor : false,
23937     editorcore : false,
23938     
23939     
23940     formats : [
23941         "p" ,  
23942         "h1","h2","h3","h4","h5","h6", 
23943         "pre", "code", 
23944         "abbr", "acronym", "address", "cite", "samp", "var",
23945         'div','span'
23946     ],
23947     
23948     onRender : function(ct, position)
23949     {
23950        // Roo.log("Call onRender: " + this.xtype);
23951         
23952        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23953        Roo.log(this.el);
23954        this.el.dom.style.marginBottom = '0';
23955        var _this = this;
23956        var editorcore = this.editorcore;
23957        var editor= this.editor;
23958        
23959        var children = [];
23960        var btn = function(id,cmd , toggle, handler, html){
23961        
23962             var  event = toggle ? 'toggle' : 'click';
23963        
23964             var a = {
23965                 size : 'sm',
23966                 xtype: 'Button',
23967                 xns: Roo.bootstrap,
23968                 glyphicon : id,
23969                 cmd : id || cmd,
23970                 enableToggle:toggle !== false,
23971                 html : html || '',
23972                 pressed : toggle ? false : null,
23973                 listeners : {}
23974             };
23975             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23976                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23977             };
23978             children.push(a);
23979             return a;
23980        }
23981        
23982     //    var cb_box = function...
23983         
23984         var style = {
23985                 xtype: 'Button',
23986                 size : 'sm',
23987                 xns: Roo.bootstrap,
23988                 glyphicon : 'font',
23989                 //html : 'submit'
23990                 menu : {
23991                     xtype: 'Menu',
23992                     xns: Roo.bootstrap,
23993                     items:  []
23994                 }
23995         };
23996         Roo.each(this.formats, function(f) {
23997             style.menu.items.push({
23998                 xtype :'MenuItem',
23999                 xns: Roo.bootstrap,
24000                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24001                 tagname : f,
24002                 listeners : {
24003                     click : function()
24004                     {
24005                         editorcore.insertTag(this.tagname);
24006                         editor.focus();
24007                     }
24008                 }
24009                 
24010             });
24011         });
24012         children.push(style);   
24013         
24014         btn('bold',false,true);
24015         btn('italic',false,true);
24016         btn('align-left', 'justifyleft',true);
24017         btn('align-center', 'justifycenter',true);
24018         btn('align-right' , 'justifyright',true);
24019         btn('link', false, false, function(btn) {
24020             //Roo.log("create link?");
24021             var url = prompt(this.createLinkText, this.defaultLinkValue);
24022             if(url && url != 'http:/'+'/'){
24023                 this.editorcore.relayCmd('createlink', url);
24024             }
24025         }),
24026         btn('list','insertunorderedlist',true);
24027         btn('pencil', false,true, function(btn){
24028                 Roo.log(this);
24029                 this.toggleSourceEdit(btn.pressed);
24030         });
24031         
24032         if (this.editor.btns.length > 0) {
24033             for (var i = 0; i<this.editor.btns.length; i++) {
24034                 children.push(this.editor.btns[i]);
24035             }
24036         }
24037         
24038         /*
24039         var cog = {
24040                 xtype: 'Button',
24041                 size : 'sm',
24042                 xns: Roo.bootstrap,
24043                 glyphicon : 'cog',
24044                 //html : 'submit'
24045                 menu : {
24046                     xtype: 'Menu',
24047                     xns: Roo.bootstrap,
24048                     items:  []
24049                 }
24050         };
24051         
24052         cog.menu.items.push({
24053             xtype :'MenuItem',
24054             xns: Roo.bootstrap,
24055             html : Clean styles,
24056             tagname : f,
24057             listeners : {
24058                 click : function()
24059                 {
24060                     editorcore.insertTag(this.tagname);
24061                     editor.focus();
24062                 }
24063             }
24064             
24065         });
24066        */
24067         
24068          
24069        this.xtype = 'NavSimplebar';
24070         
24071         for(var i=0;i< children.length;i++) {
24072             
24073             this.buttons.add(this.addxtypeChild(children[i]));
24074             
24075         }
24076         
24077         editor.on('editorevent', this.updateToolbar, this);
24078     },
24079     onBtnClick : function(id)
24080     {
24081        this.editorcore.relayCmd(id);
24082        this.editorcore.focus();
24083     },
24084     
24085     /**
24086      * Protected method that will not generally be called directly. It triggers
24087      * a toolbar update by reading the markup state of the current selection in the editor.
24088      */
24089     updateToolbar: function(){
24090
24091         if(!this.editorcore.activated){
24092             this.editor.onFirstFocus(); // is this neeed?
24093             return;
24094         }
24095
24096         var btns = this.buttons; 
24097         var doc = this.editorcore.doc;
24098         btns.get('bold').setActive(doc.queryCommandState('bold'));
24099         btns.get('italic').setActive(doc.queryCommandState('italic'));
24100         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24101         
24102         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24103         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24104         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24105         
24106         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24107         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24108          /*
24109         
24110         var ans = this.editorcore.getAllAncestors();
24111         if (this.formatCombo) {
24112             
24113             
24114             var store = this.formatCombo.store;
24115             this.formatCombo.setValue("");
24116             for (var i =0; i < ans.length;i++) {
24117                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24118                     // select it..
24119                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24120                     break;
24121                 }
24122             }
24123         }
24124         
24125         
24126         
24127         // hides menus... - so this cant be on a menu...
24128         Roo.bootstrap.MenuMgr.hideAll();
24129         */
24130         Roo.bootstrap.MenuMgr.hideAll();
24131         //this.editorsyncValue();
24132     },
24133     onFirstFocus: function() {
24134         this.buttons.each(function(item){
24135            item.enable();
24136         });
24137     },
24138     toggleSourceEdit : function(sourceEditMode){
24139         
24140           
24141         if(sourceEditMode){
24142             Roo.log("disabling buttons");
24143            this.buttons.each( function(item){
24144                 if(item.cmd != 'pencil'){
24145                     item.disable();
24146                 }
24147             });
24148           
24149         }else{
24150             Roo.log("enabling buttons");
24151             if(this.editorcore.initialized){
24152                 this.buttons.each( function(item){
24153                     item.enable();
24154                 });
24155             }
24156             
24157         }
24158         Roo.log("calling toggole on editor");
24159         // tell the editor that it's been pressed..
24160         this.editor.toggleSourceEdit(sourceEditMode);
24161        
24162     }
24163 });
24164
24165
24166
24167
24168
24169 /**
24170  * @class Roo.bootstrap.Table.AbstractSelectionModel
24171  * @extends Roo.util.Observable
24172  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24173  * implemented by descendant classes.  This class should not be directly instantiated.
24174  * @constructor
24175  */
24176 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24177     this.locked = false;
24178     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24179 };
24180
24181
24182 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24183     /** @ignore Called by the grid automatically. Do not call directly. */
24184     init : function(grid){
24185         this.grid = grid;
24186         this.initEvents();
24187     },
24188
24189     /**
24190      * Locks the selections.
24191      */
24192     lock : function(){
24193         this.locked = true;
24194     },
24195
24196     /**
24197      * Unlocks the selections.
24198      */
24199     unlock : function(){
24200         this.locked = false;
24201     },
24202
24203     /**
24204      * Returns true if the selections are locked.
24205      * @return {Boolean}
24206      */
24207     isLocked : function(){
24208         return this.locked;
24209     }
24210 });
24211 /**
24212  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24213  * @class Roo.bootstrap.Table.RowSelectionModel
24214  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24215  * It supports multiple selections and keyboard selection/navigation. 
24216  * @constructor
24217  * @param {Object} config
24218  */
24219
24220 Roo.bootstrap.Table.RowSelectionModel = function(config){
24221     Roo.apply(this, config);
24222     this.selections = new Roo.util.MixedCollection(false, function(o){
24223         return o.id;
24224     });
24225
24226     this.last = false;
24227     this.lastActive = false;
24228
24229     this.addEvents({
24230         /**
24231              * @event selectionchange
24232              * Fires when the selection changes
24233              * @param {SelectionModel} this
24234              */
24235             "selectionchange" : true,
24236         /**
24237              * @event afterselectionchange
24238              * Fires after the selection changes (eg. by key press or clicking)
24239              * @param {SelectionModel} this
24240              */
24241             "afterselectionchange" : true,
24242         /**
24243              * @event beforerowselect
24244              * Fires when a row is selected being selected, return false to cancel.
24245              * @param {SelectionModel} this
24246              * @param {Number} rowIndex The selected index
24247              * @param {Boolean} keepExisting False if other selections will be cleared
24248              */
24249             "beforerowselect" : true,
24250         /**
24251              * @event rowselect
24252              * Fires when a row is selected.
24253              * @param {SelectionModel} this
24254              * @param {Number} rowIndex The selected index
24255              * @param {Roo.data.Record} r The record
24256              */
24257             "rowselect" : true,
24258         /**
24259              * @event rowdeselect
24260              * Fires when a row is deselected.
24261              * @param {SelectionModel} this
24262              * @param {Number} rowIndex The selected index
24263              */
24264         "rowdeselect" : true
24265     });
24266     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24267     this.locked = false;
24268  };
24269
24270 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24271     /**
24272      * @cfg {Boolean} singleSelect
24273      * True to allow selection of only one row at a time (defaults to false)
24274      */
24275     singleSelect : false,
24276
24277     // private
24278     initEvents : function()
24279     {
24280
24281         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24282         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24283         //}else{ // allow click to work like normal
24284          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24285         //}
24286         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24287         this.grid.on("rowclick", this.handleMouseDown, this);
24288         
24289         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24290             "up" : function(e){
24291                 if(!e.shiftKey){
24292                     this.selectPrevious(e.shiftKey);
24293                 }else if(this.last !== false && this.lastActive !== false){
24294                     var last = this.last;
24295                     this.selectRange(this.last,  this.lastActive-1);
24296                     this.grid.getView().focusRow(this.lastActive);
24297                     if(last !== false){
24298                         this.last = last;
24299                     }
24300                 }else{
24301                     this.selectFirstRow();
24302                 }
24303                 this.fireEvent("afterselectionchange", this);
24304             },
24305             "down" : function(e){
24306                 if(!e.shiftKey){
24307                     this.selectNext(e.shiftKey);
24308                 }else if(this.last !== false && this.lastActive !== false){
24309                     var last = this.last;
24310                     this.selectRange(this.last,  this.lastActive+1);
24311                     this.grid.getView().focusRow(this.lastActive);
24312                     if(last !== false){
24313                         this.last = last;
24314                     }
24315                 }else{
24316                     this.selectFirstRow();
24317                 }
24318                 this.fireEvent("afterselectionchange", this);
24319             },
24320             scope: this
24321         });
24322         this.grid.store.on('load', function(){
24323             this.selections.clear();
24324         },this);
24325         /*
24326         var view = this.grid.view;
24327         view.on("refresh", this.onRefresh, this);
24328         view.on("rowupdated", this.onRowUpdated, this);
24329         view.on("rowremoved", this.onRemove, this);
24330         */
24331     },
24332
24333     // private
24334     onRefresh : function()
24335     {
24336         var ds = this.grid.store, i, v = this.grid.view;
24337         var s = this.selections;
24338         s.each(function(r){
24339             if((i = ds.indexOfId(r.id)) != -1){
24340                 v.onRowSelect(i);
24341             }else{
24342                 s.remove(r);
24343             }
24344         });
24345     },
24346
24347     // private
24348     onRemove : function(v, index, r){
24349         this.selections.remove(r);
24350     },
24351
24352     // private
24353     onRowUpdated : function(v, index, r){
24354         if(this.isSelected(r)){
24355             v.onRowSelect(index);
24356         }
24357     },
24358
24359     /**
24360      * Select records.
24361      * @param {Array} records The records to select
24362      * @param {Boolean} keepExisting (optional) True to keep existing selections
24363      */
24364     selectRecords : function(records, keepExisting)
24365     {
24366         if(!keepExisting){
24367             this.clearSelections();
24368         }
24369             var ds = this.grid.store;
24370         for(var i = 0, len = records.length; i < len; i++){
24371             this.selectRow(ds.indexOf(records[i]), true);
24372         }
24373     },
24374
24375     /**
24376      * Gets the number of selected rows.
24377      * @return {Number}
24378      */
24379     getCount : function(){
24380         return this.selections.length;
24381     },
24382
24383     /**
24384      * Selects the first row in the grid.
24385      */
24386     selectFirstRow : function(){
24387         this.selectRow(0);
24388     },
24389
24390     /**
24391      * Select the last row.
24392      * @param {Boolean} keepExisting (optional) True to keep existing selections
24393      */
24394     selectLastRow : function(keepExisting){
24395         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24396         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24397     },
24398
24399     /**
24400      * Selects the row immediately following the last selected row.
24401      * @param {Boolean} keepExisting (optional) True to keep existing selections
24402      */
24403     selectNext : function(keepExisting)
24404     {
24405             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24406             this.selectRow(this.last+1, keepExisting);
24407             this.grid.getView().focusRow(this.last);
24408         }
24409     },
24410
24411     /**
24412      * Selects the row that precedes the last selected row.
24413      * @param {Boolean} keepExisting (optional) True to keep existing selections
24414      */
24415     selectPrevious : function(keepExisting){
24416         if(this.last){
24417             this.selectRow(this.last-1, keepExisting);
24418             this.grid.getView().focusRow(this.last);
24419         }
24420     },
24421
24422     /**
24423      * Returns the selected records
24424      * @return {Array} Array of selected records
24425      */
24426     getSelections : function(){
24427         return [].concat(this.selections.items);
24428     },
24429
24430     /**
24431      * Returns the first selected record.
24432      * @return {Record}
24433      */
24434     getSelected : function(){
24435         return this.selections.itemAt(0);
24436     },
24437
24438
24439     /**
24440      * Clears all selections.
24441      */
24442     clearSelections : function(fast)
24443     {
24444         if(this.locked) {
24445             return;
24446         }
24447         if(fast !== true){
24448                 var ds = this.grid.store;
24449             var s = this.selections;
24450             s.each(function(r){
24451                 this.deselectRow(ds.indexOfId(r.id));
24452             }, this);
24453             s.clear();
24454         }else{
24455             this.selections.clear();
24456         }
24457         this.last = false;
24458     },
24459
24460
24461     /**
24462      * Selects all rows.
24463      */
24464     selectAll : function(){
24465         if(this.locked) {
24466             return;
24467         }
24468         this.selections.clear();
24469         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24470             this.selectRow(i, true);
24471         }
24472     },
24473
24474     /**
24475      * Returns True if there is a selection.
24476      * @return {Boolean}
24477      */
24478     hasSelection : function(){
24479         return this.selections.length > 0;
24480     },
24481
24482     /**
24483      * Returns True if the specified row is selected.
24484      * @param {Number/Record} record The record or index of the record to check
24485      * @return {Boolean}
24486      */
24487     isSelected : function(index){
24488             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24489         return (r && this.selections.key(r.id) ? true : false);
24490     },
24491
24492     /**
24493      * Returns True if the specified record id is selected.
24494      * @param {String} id The id of record to check
24495      * @return {Boolean}
24496      */
24497     isIdSelected : function(id){
24498         return (this.selections.key(id) ? true : false);
24499     },
24500
24501
24502     // private
24503     handleMouseDBClick : function(e, t){
24504         
24505     },
24506     // private
24507     handleMouseDown : function(e, t)
24508     {
24509             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24510         if(this.isLocked() || rowIndex < 0 ){
24511             return;
24512         };
24513         if(e.shiftKey && this.last !== false){
24514             var last = this.last;
24515             this.selectRange(last, rowIndex, e.ctrlKey);
24516             this.last = last; // reset the last
24517             t.focus();
24518     
24519         }else{
24520             var isSelected = this.isSelected(rowIndex);
24521             //Roo.log("select row:" + rowIndex);
24522             if(isSelected){
24523                 this.deselectRow(rowIndex);
24524             } else {
24525                         this.selectRow(rowIndex, true);
24526             }
24527     
24528             /*
24529                 if(e.button !== 0 && isSelected){
24530                 alert('rowIndex 2: ' + rowIndex);
24531                     view.focusRow(rowIndex);
24532                 }else if(e.ctrlKey && isSelected){
24533                     this.deselectRow(rowIndex);
24534                 }else if(!isSelected){
24535                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24536                     view.focusRow(rowIndex);
24537                 }
24538             */
24539         }
24540         this.fireEvent("afterselectionchange", this);
24541     },
24542     // private
24543     handleDragableRowClick :  function(grid, rowIndex, e) 
24544     {
24545         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24546             this.selectRow(rowIndex, false);
24547             grid.view.focusRow(rowIndex);
24548              this.fireEvent("afterselectionchange", this);
24549         }
24550     },
24551     
24552     /**
24553      * Selects multiple rows.
24554      * @param {Array} rows Array of the indexes of the row to select
24555      * @param {Boolean} keepExisting (optional) True to keep existing selections
24556      */
24557     selectRows : function(rows, keepExisting){
24558         if(!keepExisting){
24559             this.clearSelections();
24560         }
24561         for(var i = 0, len = rows.length; i < len; i++){
24562             this.selectRow(rows[i], true);
24563         }
24564     },
24565
24566     /**
24567      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24568      * @param {Number} startRow The index of the first row in the range
24569      * @param {Number} endRow The index of the last row in the range
24570      * @param {Boolean} keepExisting (optional) True to retain existing selections
24571      */
24572     selectRange : function(startRow, endRow, keepExisting){
24573         if(this.locked) {
24574             return;
24575         }
24576         if(!keepExisting){
24577             this.clearSelections();
24578         }
24579         if(startRow <= endRow){
24580             for(var i = startRow; i <= endRow; i++){
24581                 this.selectRow(i, true);
24582             }
24583         }else{
24584             for(var i = startRow; i >= endRow; i--){
24585                 this.selectRow(i, true);
24586             }
24587         }
24588     },
24589
24590     /**
24591      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24592      * @param {Number} startRow The index of the first row in the range
24593      * @param {Number} endRow The index of the last row in the range
24594      */
24595     deselectRange : function(startRow, endRow, preventViewNotify){
24596         if(this.locked) {
24597             return;
24598         }
24599         for(var i = startRow; i <= endRow; i++){
24600             this.deselectRow(i, preventViewNotify);
24601         }
24602     },
24603
24604     /**
24605      * Selects a row.
24606      * @param {Number} row The index of the row to select
24607      * @param {Boolean} keepExisting (optional) True to keep existing selections
24608      */
24609     selectRow : function(index, keepExisting, preventViewNotify)
24610     {
24611             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24612             return;
24613         }
24614         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24615             if(!keepExisting || this.singleSelect){
24616                 this.clearSelections();
24617             }
24618             
24619             var r = this.grid.store.getAt(index);
24620             //console.log('selectRow - record id :' + r.id);
24621             
24622             this.selections.add(r);
24623             this.last = this.lastActive = index;
24624             if(!preventViewNotify){
24625                 var proxy = new Roo.Element(
24626                                 this.grid.getRowDom(index)
24627                 );
24628                 proxy.addClass('bg-info info');
24629             }
24630             this.fireEvent("rowselect", this, index, r);
24631             this.fireEvent("selectionchange", this);
24632         }
24633     },
24634
24635     /**
24636      * Deselects a row.
24637      * @param {Number} row The index of the row to deselect
24638      */
24639     deselectRow : function(index, preventViewNotify)
24640     {
24641         if(this.locked) {
24642             return;
24643         }
24644         if(this.last == index){
24645             this.last = false;
24646         }
24647         if(this.lastActive == index){
24648             this.lastActive = false;
24649         }
24650         
24651         var r = this.grid.store.getAt(index);
24652         if (!r) {
24653             return;
24654         }
24655         
24656         this.selections.remove(r);
24657         //.console.log('deselectRow - record id :' + r.id);
24658         if(!preventViewNotify){
24659         
24660             var proxy = new Roo.Element(
24661                 this.grid.getRowDom(index)
24662             );
24663             proxy.removeClass('bg-info info');
24664         }
24665         this.fireEvent("rowdeselect", this, index);
24666         this.fireEvent("selectionchange", this);
24667     },
24668
24669     // private
24670     restoreLast : function(){
24671         if(this._last){
24672             this.last = this._last;
24673         }
24674     },
24675
24676     // private
24677     acceptsNav : function(row, col, cm){
24678         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24679     },
24680
24681     // private
24682     onEditorKey : function(field, e){
24683         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24684         if(k == e.TAB){
24685             e.stopEvent();
24686             ed.completeEdit();
24687             if(e.shiftKey){
24688                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24689             }else{
24690                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24691             }
24692         }else if(k == e.ENTER && !e.ctrlKey){
24693             e.stopEvent();
24694             ed.completeEdit();
24695             if(e.shiftKey){
24696                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24697             }else{
24698                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24699             }
24700         }else if(k == e.ESC){
24701             ed.cancelEdit();
24702         }
24703         if(newCell){
24704             g.startEditing(newCell[0], newCell[1]);
24705         }
24706     }
24707 });
24708 /*
24709  * Based on:
24710  * Ext JS Library 1.1.1
24711  * Copyright(c) 2006-2007, Ext JS, LLC.
24712  *
24713  * Originally Released Under LGPL - original licence link has changed is not relivant.
24714  *
24715  * Fork - LGPL
24716  * <script type="text/javascript">
24717  */
24718  
24719 /**
24720  * @class Roo.bootstrap.PagingToolbar
24721  * @extends Roo.bootstrap.NavSimplebar
24722  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24723  * @constructor
24724  * Create a new PagingToolbar
24725  * @param {Object} config The config object
24726  * @param {Roo.data.Store} store
24727  */
24728 Roo.bootstrap.PagingToolbar = function(config)
24729 {
24730     // old args format still supported... - xtype is prefered..
24731         // created from xtype...
24732     
24733     this.ds = config.dataSource;
24734     
24735     if (config.store && !this.ds) {
24736         this.store= Roo.factory(config.store, Roo.data);
24737         this.ds = this.store;
24738         this.ds.xmodule = this.xmodule || false;
24739     }
24740     
24741     this.toolbarItems = [];
24742     if (config.items) {
24743         this.toolbarItems = config.items;
24744     }
24745     
24746     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24747     
24748     this.cursor = 0;
24749     
24750     if (this.ds) { 
24751         this.bind(this.ds);
24752     }
24753     
24754     if (Roo.bootstrap.version == 4) {
24755         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24756     } else {
24757         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24758     }
24759     
24760 };
24761
24762 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24763     /**
24764      * @cfg {Roo.data.Store} dataSource
24765      * The underlying data store providing the paged data
24766      */
24767     /**
24768      * @cfg {String/HTMLElement/Element} container
24769      * container The id or element that will contain the toolbar
24770      */
24771     /**
24772      * @cfg {Boolean} displayInfo
24773      * True to display the displayMsg (defaults to false)
24774      */
24775     /**
24776      * @cfg {Number} pageSize
24777      * The number of records to display per page (defaults to 20)
24778      */
24779     pageSize: 20,
24780     /**
24781      * @cfg {String} displayMsg
24782      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24783      */
24784     displayMsg : 'Displaying {0} - {1} of {2}',
24785     /**
24786      * @cfg {String} emptyMsg
24787      * The message to display when no records are found (defaults to "No data to display")
24788      */
24789     emptyMsg : 'No data to display',
24790     /**
24791      * Customizable piece of the default paging text (defaults to "Page")
24792      * @type String
24793      */
24794     beforePageText : "Page",
24795     /**
24796      * Customizable piece of the default paging text (defaults to "of %0")
24797      * @type String
24798      */
24799     afterPageText : "of {0}",
24800     /**
24801      * Customizable piece of the default paging text (defaults to "First Page")
24802      * @type String
24803      */
24804     firstText : "First Page",
24805     /**
24806      * Customizable piece of the default paging text (defaults to "Previous Page")
24807      * @type String
24808      */
24809     prevText : "Previous Page",
24810     /**
24811      * Customizable piece of the default paging text (defaults to "Next Page")
24812      * @type String
24813      */
24814     nextText : "Next Page",
24815     /**
24816      * Customizable piece of the default paging text (defaults to "Last Page")
24817      * @type String
24818      */
24819     lastText : "Last Page",
24820     /**
24821      * Customizable piece of the default paging text (defaults to "Refresh")
24822      * @type String
24823      */
24824     refreshText : "Refresh",
24825
24826     buttons : false,
24827     // private
24828     onRender : function(ct, position) 
24829     {
24830         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24831         this.navgroup.parentId = this.id;
24832         this.navgroup.onRender(this.el, null);
24833         // add the buttons to the navgroup
24834         
24835         if(this.displayInfo){
24836             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24837             this.displayEl = this.el.select('.x-paging-info', true).first();
24838 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24839 //            this.displayEl = navel.el.select('span',true).first();
24840         }
24841         
24842         var _this = this;
24843         
24844         if(this.buttons){
24845             Roo.each(_this.buttons, function(e){ // this might need to use render????
24846                Roo.factory(e).render(_this.el);
24847             });
24848         }
24849             
24850         Roo.each(_this.toolbarItems, function(e) {
24851             _this.navgroup.addItem(e);
24852         });
24853         
24854         
24855         this.first = this.navgroup.addItem({
24856             tooltip: this.firstText,
24857             cls: "prev btn-outline-secondary",
24858             html : ' <i class="fa fa-step-backward"></i>',
24859             disabled: true,
24860             preventDefault: true,
24861             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24862         });
24863         
24864         this.prev =  this.navgroup.addItem({
24865             tooltip: this.prevText,
24866             cls: "prev btn-outline-secondary",
24867             html : ' <i class="fa fa-backward"></i>',
24868             disabled: true,
24869             preventDefault: true,
24870             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24871         });
24872     //this.addSeparator();
24873         
24874         
24875         var field = this.navgroup.addItem( {
24876             tagtype : 'span',
24877             cls : 'x-paging-position  btn-outline-secondary',
24878              disabled: true,
24879             html : this.beforePageText  +
24880                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24881                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24882          } ); //?? escaped?
24883         
24884         this.field = field.el.select('input', true).first();
24885         this.field.on("keydown", this.onPagingKeydown, this);
24886         this.field.on("focus", function(){this.dom.select();});
24887     
24888     
24889         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24890         //this.field.setHeight(18);
24891         //this.addSeparator();
24892         this.next = this.navgroup.addItem({
24893             tooltip: this.nextText,
24894             cls: "next btn-outline-secondary",
24895             html : ' <i class="fa fa-forward"></i>',
24896             disabled: true,
24897             preventDefault: true,
24898             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24899         });
24900         this.last = this.navgroup.addItem({
24901             tooltip: this.lastText,
24902             html : ' <i class="fa fa-step-forward"></i>',
24903             cls: "next btn-outline-secondary",
24904             disabled: true,
24905             preventDefault: true,
24906             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24907         });
24908     //this.addSeparator();
24909         this.loading = this.navgroup.addItem({
24910             tooltip: this.refreshText,
24911             cls: "btn-outline-secondary",
24912             html : ' <i class="fa fa-refresh"></i>',
24913             preventDefault: true,
24914             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24915         });
24916         
24917     },
24918
24919     // private
24920     updateInfo : function(){
24921         if(this.displayEl){
24922             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24923             var msg = count == 0 ?
24924                 this.emptyMsg :
24925                 String.format(
24926                     this.displayMsg,
24927                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24928                 );
24929             this.displayEl.update(msg);
24930         }
24931     },
24932
24933     // private
24934     onLoad : function(ds, r, o)
24935     {
24936         this.cursor = o.params.start ? o.params.start : 0;
24937         
24938         var d = this.getPageData(),
24939             ap = d.activePage,
24940             ps = d.pages;
24941         
24942         
24943         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24944         this.field.dom.value = ap;
24945         this.first.setDisabled(ap == 1);
24946         this.prev.setDisabled(ap == 1);
24947         this.next.setDisabled(ap == ps);
24948         this.last.setDisabled(ap == ps);
24949         this.loading.enable();
24950         this.updateInfo();
24951     },
24952
24953     // private
24954     getPageData : function(){
24955         var total = this.ds.getTotalCount();
24956         return {
24957             total : total,
24958             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24959             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24960         };
24961     },
24962
24963     // private
24964     onLoadError : function(){
24965         this.loading.enable();
24966     },
24967
24968     // private
24969     onPagingKeydown : function(e){
24970         var k = e.getKey();
24971         var d = this.getPageData();
24972         if(k == e.RETURN){
24973             var v = this.field.dom.value, pageNum;
24974             if(!v || isNaN(pageNum = parseInt(v, 10))){
24975                 this.field.dom.value = d.activePage;
24976                 return;
24977             }
24978             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24979             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24980             e.stopEvent();
24981         }
24982         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))
24983         {
24984           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24985           this.field.dom.value = pageNum;
24986           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24987           e.stopEvent();
24988         }
24989         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24990         {
24991           var v = this.field.dom.value, pageNum; 
24992           var increment = (e.shiftKey) ? 10 : 1;
24993           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24994                 increment *= -1;
24995           }
24996           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24997             this.field.dom.value = d.activePage;
24998             return;
24999           }
25000           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25001           {
25002             this.field.dom.value = parseInt(v, 10) + increment;
25003             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25004             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25005           }
25006           e.stopEvent();
25007         }
25008     },
25009
25010     // private
25011     beforeLoad : function(){
25012         if(this.loading){
25013             this.loading.disable();
25014         }
25015     },
25016
25017     // private
25018     onClick : function(which){
25019         
25020         var ds = this.ds;
25021         if (!ds) {
25022             return;
25023         }
25024         
25025         switch(which){
25026             case "first":
25027                 ds.load({params:{start: 0, limit: this.pageSize}});
25028             break;
25029             case "prev":
25030                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25031             break;
25032             case "next":
25033                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25034             break;
25035             case "last":
25036                 var total = ds.getTotalCount();
25037                 var extra = total % this.pageSize;
25038                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25039                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25040             break;
25041             case "refresh":
25042                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25043             break;
25044         }
25045     },
25046
25047     /**
25048      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25049      * @param {Roo.data.Store} store The data store to unbind
25050      */
25051     unbind : function(ds){
25052         ds.un("beforeload", this.beforeLoad, this);
25053         ds.un("load", this.onLoad, this);
25054         ds.un("loadexception", this.onLoadError, this);
25055         ds.un("remove", this.updateInfo, this);
25056         ds.un("add", this.updateInfo, this);
25057         this.ds = undefined;
25058     },
25059
25060     /**
25061      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25062      * @param {Roo.data.Store} store The data store to bind
25063      */
25064     bind : function(ds){
25065         ds.on("beforeload", this.beforeLoad, this);
25066         ds.on("load", this.onLoad, this);
25067         ds.on("loadexception", this.onLoadError, this);
25068         ds.on("remove", this.updateInfo, this);
25069         ds.on("add", this.updateInfo, this);
25070         this.ds = ds;
25071     }
25072 });/*
25073  * - LGPL
25074  *
25075  * element
25076  * 
25077  */
25078
25079 /**
25080  * @class Roo.bootstrap.MessageBar
25081  * @extends Roo.bootstrap.Component
25082  * Bootstrap MessageBar class
25083  * @cfg {String} html contents of the MessageBar
25084  * @cfg {String} weight (info | success | warning | danger) default info
25085  * @cfg {String} beforeClass insert the bar before the given class
25086  * @cfg {Boolean} closable (true | false) default false
25087  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25088  * 
25089  * @constructor
25090  * Create a new Element
25091  * @param {Object} config The config object
25092  */
25093
25094 Roo.bootstrap.MessageBar = function(config){
25095     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25096 };
25097
25098 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25099     
25100     html: '',
25101     weight: 'info',
25102     closable: false,
25103     fixed: false,
25104     beforeClass: 'bootstrap-sticky-wrap',
25105     
25106     getAutoCreate : function(){
25107         
25108         var cfg = {
25109             tag: 'div',
25110             cls: 'alert alert-dismissable alert-' + this.weight,
25111             cn: [
25112                 {
25113                     tag: 'span',
25114                     cls: 'message',
25115                     html: this.html || ''
25116                 }
25117             ]
25118         };
25119         
25120         if(this.fixed){
25121             cfg.cls += ' alert-messages-fixed';
25122         }
25123         
25124         if(this.closable){
25125             cfg.cn.push({
25126                 tag: 'button',
25127                 cls: 'close',
25128                 html: 'x'
25129             });
25130         }
25131         
25132         return cfg;
25133     },
25134     
25135     onRender : function(ct, position)
25136     {
25137         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25138         
25139         if(!this.el){
25140             var cfg = Roo.apply({},  this.getAutoCreate());
25141             cfg.id = Roo.id();
25142             
25143             if (this.cls) {
25144                 cfg.cls += ' ' + this.cls;
25145             }
25146             if (this.style) {
25147                 cfg.style = this.style;
25148             }
25149             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25150             
25151             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25152         }
25153         
25154         this.el.select('>button.close').on('click', this.hide, this);
25155         
25156     },
25157     
25158     show : function()
25159     {
25160         if (!this.rendered) {
25161             this.render();
25162         }
25163         
25164         this.el.show();
25165         
25166         this.fireEvent('show', this);
25167         
25168     },
25169     
25170     hide : function()
25171     {
25172         if (!this.rendered) {
25173             this.render();
25174         }
25175         
25176         this.el.hide();
25177         
25178         this.fireEvent('hide', this);
25179     },
25180     
25181     update : function()
25182     {
25183 //        var e = this.el.dom.firstChild;
25184 //        
25185 //        if(this.closable){
25186 //            e = e.nextSibling;
25187 //        }
25188 //        
25189 //        e.data = this.html || '';
25190
25191         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25192     }
25193    
25194 });
25195
25196  
25197
25198      /*
25199  * - LGPL
25200  *
25201  * Graph
25202  * 
25203  */
25204
25205
25206 /**
25207  * @class Roo.bootstrap.Graph
25208  * @extends Roo.bootstrap.Component
25209  * Bootstrap Graph class
25210 > Prameters
25211  -sm {number} sm 4
25212  -md {number} md 5
25213  @cfg {String} graphtype  bar | vbar | pie
25214  @cfg {number} g_x coodinator | centre x (pie)
25215  @cfg {number} g_y coodinator | centre y (pie)
25216  @cfg {number} g_r radius (pie)
25217  @cfg {number} g_height height of the chart (respected by all elements in the set)
25218  @cfg {number} g_width width of the chart (respected by all elements in the set)
25219  @cfg {Object} title The title of the chart
25220     
25221  -{Array}  values
25222  -opts (object) options for the chart 
25223      o {
25224      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25225      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25226      o vgutter (number)
25227      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.
25228      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25229      o to
25230      o stretch (boolean)
25231      o }
25232  -opts (object) options for the pie
25233      o{
25234      o cut
25235      o startAngle (number)
25236      o endAngle (number)
25237      } 
25238  *
25239  * @constructor
25240  * Create a new Input
25241  * @param {Object} config The config object
25242  */
25243
25244 Roo.bootstrap.Graph = function(config){
25245     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25246     
25247     this.addEvents({
25248         // img events
25249         /**
25250          * @event click
25251          * The img click event for the img.
25252          * @param {Roo.EventObject} e
25253          */
25254         "click" : true
25255     });
25256 };
25257
25258 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25259     
25260     sm: 4,
25261     md: 5,
25262     graphtype: 'bar',
25263     g_height: 250,
25264     g_width: 400,
25265     g_x: 50,
25266     g_y: 50,
25267     g_r: 30,
25268     opts:{
25269         //g_colors: this.colors,
25270         g_type: 'soft',
25271         g_gutter: '20%'
25272
25273     },
25274     title : false,
25275
25276     getAutoCreate : function(){
25277         
25278         var cfg = {
25279             tag: 'div',
25280             html : null
25281         };
25282         
25283         
25284         return  cfg;
25285     },
25286
25287     onRender : function(ct,position){
25288         
25289         
25290         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25291         
25292         if (typeof(Raphael) == 'undefined') {
25293             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25294             return;
25295         }
25296         
25297         this.raphael = Raphael(this.el.dom);
25298         
25299                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25300                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25301                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25302                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25303                 /*
25304                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25305                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25306                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25307                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25308                 
25309                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25310                 r.barchart(330, 10, 300, 220, data1);
25311                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25312                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25313                 */
25314                 
25315                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25316                 // r.barchart(30, 30, 560, 250,  xdata, {
25317                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25318                 //     axis : "0 0 1 1",
25319                 //     axisxlabels :  xdata
25320                 //     //yvalues : cols,
25321                    
25322                 // });
25323 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25324 //        
25325 //        this.load(null,xdata,{
25326 //                axis : "0 0 1 1",
25327 //                axisxlabels :  xdata
25328 //                });
25329
25330     },
25331
25332     load : function(graphtype,xdata,opts)
25333     {
25334         this.raphael.clear();
25335         if(!graphtype) {
25336             graphtype = this.graphtype;
25337         }
25338         if(!opts){
25339             opts = this.opts;
25340         }
25341         var r = this.raphael,
25342             fin = function () {
25343                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25344             },
25345             fout = function () {
25346                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25347             },
25348             pfin = function() {
25349                 this.sector.stop();
25350                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25351
25352                 if (this.label) {
25353                     this.label[0].stop();
25354                     this.label[0].attr({ r: 7.5 });
25355                     this.label[1].attr({ "font-weight": 800 });
25356                 }
25357             },
25358             pfout = function() {
25359                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25360
25361                 if (this.label) {
25362                     this.label[0].animate({ r: 5 }, 500, "bounce");
25363                     this.label[1].attr({ "font-weight": 400 });
25364                 }
25365             };
25366
25367         switch(graphtype){
25368             case 'bar':
25369                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25370                 break;
25371             case 'hbar':
25372                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25373                 break;
25374             case 'pie':
25375 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25376 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25377 //            
25378                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25379                 
25380                 break;
25381
25382         }
25383         
25384         if(this.title){
25385             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25386         }
25387         
25388     },
25389     
25390     setTitle: function(o)
25391     {
25392         this.title = o;
25393     },
25394     
25395     initEvents: function() {
25396         
25397         if(!this.href){
25398             this.el.on('click', this.onClick, this);
25399         }
25400     },
25401     
25402     onClick : function(e)
25403     {
25404         Roo.log('img onclick');
25405         this.fireEvent('click', this, e);
25406     }
25407    
25408 });
25409
25410  
25411 /*
25412  * - LGPL
25413  *
25414  * numberBox
25415  * 
25416  */
25417 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25418
25419 /**
25420  * @class Roo.bootstrap.dash.NumberBox
25421  * @extends Roo.bootstrap.Component
25422  * Bootstrap NumberBox class
25423  * @cfg {String} headline Box headline
25424  * @cfg {String} content Box content
25425  * @cfg {String} icon Box icon
25426  * @cfg {String} footer Footer text
25427  * @cfg {String} fhref Footer href
25428  * 
25429  * @constructor
25430  * Create a new NumberBox
25431  * @param {Object} config The config object
25432  */
25433
25434
25435 Roo.bootstrap.dash.NumberBox = function(config){
25436     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25437     
25438 };
25439
25440 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25441     
25442     headline : '',
25443     content : '',
25444     icon : '',
25445     footer : '',
25446     fhref : '',
25447     ficon : '',
25448     
25449     getAutoCreate : function(){
25450         
25451         var cfg = {
25452             tag : 'div',
25453             cls : 'small-box ',
25454             cn : [
25455                 {
25456                     tag : 'div',
25457                     cls : 'inner',
25458                     cn :[
25459                         {
25460                             tag : 'h3',
25461                             cls : 'roo-headline',
25462                             html : this.headline
25463                         },
25464                         {
25465                             tag : 'p',
25466                             cls : 'roo-content',
25467                             html : this.content
25468                         }
25469                     ]
25470                 }
25471             ]
25472         };
25473         
25474         if(this.icon){
25475             cfg.cn.push({
25476                 tag : 'div',
25477                 cls : 'icon',
25478                 cn :[
25479                     {
25480                         tag : 'i',
25481                         cls : 'ion ' + this.icon
25482                     }
25483                 ]
25484             });
25485         }
25486         
25487         if(this.footer){
25488             var footer = {
25489                 tag : 'a',
25490                 cls : 'small-box-footer',
25491                 href : this.fhref || '#',
25492                 html : this.footer
25493             };
25494             
25495             cfg.cn.push(footer);
25496             
25497         }
25498         
25499         return  cfg;
25500     },
25501
25502     onRender : function(ct,position){
25503         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25504
25505
25506        
25507                 
25508     },
25509
25510     setHeadline: function (value)
25511     {
25512         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25513     },
25514     
25515     setFooter: function (value, href)
25516     {
25517         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25518         
25519         if(href){
25520             this.el.select('a.small-box-footer',true).first().attr('href', href);
25521         }
25522         
25523     },
25524
25525     setContent: function (value)
25526     {
25527         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25528     },
25529
25530     initEvents: function() 
25531     {   
25532         
25533     }
25534     
25535 });
25536
25537  
25538 /*
25539  * - LGPL
25540  *
25541  * TabBox
25542  * 
25543  */
25544 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25545
25546 /**
25547  * @class Roo.bootstrap.dash.TabBox
25548  * @extends Roo.bootstrap.Component
25549  * Bootstrap TabBox class
25550  * @cfg {String} title Title of the TabBox
25551  * @cfg {String} icon Icon of the TabBox
25552  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25553  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25554  * 
25555  * @constructor
25556  * Create a new TabBox
25557  * @param {Object} config The config object
25558  */
25559
25560
25561 Roo.bootstrap.dash.TabBox = function(config){
25562     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25563     this.addEvents({
25564         // raw events
25565         /**
25566          * @event addpane
25567          * When a pane is added
25568          * @param {Roo.bootstrap.dash.TabPane} pane
25569          */
25570         "addpane" : true,
25571         /**
25572          * @event activatepane
25573          * When a pane is activated
25574          * @param {Roo.bootstrap.dash.TabPane} pane
25575          */
25576         "activatepane" : true
25577         
25578          
25579     });
25580     
25581     this.panes = [];
25582 };
25583
25584 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25585
25586     title : '',
25587     icon : false,
25588     showtabs : true,
25589     tabScrollable : false,
25590     
25591     getChildContainer : function()
25592     {
25593         return this.el.select('.tab-content', true).first();
25594     },
25595     
25596     getAutoCreate : function(){
25597         
25598         var header = {
25599             tag: 'li',
25600             cls: 'pull-left header',
25601             html: this.title,
25602             cn : []
25603         };
25604         
25605         if(this.icon){
25606             header.cn.push({
25607                 tag: 'i',
25608                 cls: 'fa ' + this.icon
25609             });
25610         }
25611         
25612         var h = {
25613             tag: 'ul',
25614             cls: 'nav nav-tabs pull-right',
25615             cn: [
25616                 header
25617             ]
25618         };
25619         
25620         if(this.tabScrollable){
25621             h = {
25622                 tag: 'div',
25623                 cls: 'tab-header',
25624                 cn: [
25625                     {
25626                         tag: 'ul',
25627                         cls: 'nav nav-tabs pull-right',
25628                         cn: [
25629                             header
25630                         ]
25631                     }
25632                 ]
25633             };
25634         }
25635         
25636         var cfg = {
25637             tag: 'div',
25638             cls: 'nav-tabs-custom',
25639             cn: [
25640                 h,
25641                 {
25642                     tag: 'div',
25643                     cls: 'tab-content no-padding',
25644                     cn: []
25645                 }
25646             ]
25647         };
25648
25649         return  cfg;
25650     },
25651     initEvents : function()
25652     {
25653         //Roo.log('add add pane handler');
25654         this.on('addpane', this.onAddPane, this);
25655     },
25656      /**
25657      * Updates the box title
25658      * @param {String} html to set the title to.
25659      */
25660     setTitle : function(value)
25661     {
25662         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25663     },
25664     onAddPane : function(pane)
25665     {
25666         this.panes.push(pane);
25667         //Roo.log('addpane');
25668         //Roo.log(pane);
25669         // tabs are rendere left to right..
25670         if(!this.showtabs){
25671             return;
25672         }
25673         
25674         var ctr = this.el.select('.nav-tabs', true).first();
25675          
25676          
25677         var existing = ctr.select('.nav-tab',true);
25678         var qty = existing.getCount();;
25679         
25680         
25681         var tab = ctr.createChild({
25682             tag : 'li',
25683             cls : 'nav-tab' + (qty ? '' : ' active'),
25684             cn : [
25685                 {
25686                     tag : 'a',
25687                     href:'#',
25688                     html : pane.title
25689                 }
25690             ]
25691         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25692         pane.tab = tab;
25693         
25694         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25695         if (!qty) {
25696             pane.el.addClass('active');
25697         }
25698         
25699                 
25700     },
25701     onTabClick : function(ev,un,ob,pane)
25702     {
25703         //Roo.log('tab - prev default');
25704         ev.preventDefault();
25705         
25706         
25707         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25708         pane.tab.addClass('active');
25709         //Roo.log(pane.title);
25710         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25711         // technically we should have a deactivate event.. but maybe add later.
25712         // and it should not de-activate the selected tab...
25713         this.fireEvent('activatepane', pane);
25714         pane.el.addClass('active');
25715         pane.fireEvent('activate');
25716         
25717         
25718     },
25719     
25720     getActivePane : function()
25721     {
25722         var r = false;
25723         Roo.each(this.panes, function(p) {
25724             if(p.el.hasClass('active')){
25725                 r = p;
25726                 return false;
25727             }
25728             
25729             return;
25730         });
25731         
25732         return r;
25733     }
25734     
25735     
25736 });
25737
25738  
25739 /*
25740  * - LGPL
25741  *
25742  * Tab pane
25743  * 
25744  */
25745 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25746 /**
25747  * @class Roo.bootstrap.TabPane
25748  * @extends Roo.bootstrap.Component
25749  * Bootstrap TabPane class
25750  * @cfg {Boolean} active (false | true) Default false
25751  * @cfg {String} title title of panel
25752
25753  * 
25754  * @constructor
25755  * Create a new TabPane
25756  * @param {Object} config The config object
25757  */
25758
25759 Roo.bootstrap.dash.TabPane = function(config){
25760     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25761     
25762     this.addEvents({
25763         // raw events
25764         /**
25765          * @event activate
25766          * When a pane is activated
25767          * @param {Roo.bootstrap.dash.TabPane} pane
25768          */
25769         "activate" : true
25770          
25771     });
25772 };
25773
25774 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25775     
25776     active : false,
25777     title : '',
25778     
25779     // the tabBox that this is attached to.
25780     tab : false,
25781      
25782     getAutoCreate : function() 
25783     {
25784         var cfg = {
25785             tag: 'div',
25786             cls: 'tab-pane'
25787         };
25788         
25789         if(this.active){
25790             cfg.cls += ' active';
25791         }
25792         
25793         return cfg;
25794     },
25795     initEvents  : function()
25796     {
25797         //Roo.log('trigger add pane handler');
25798         this.parent().fireEvent('addpane', this)
25799     },
25800     
25801      /**
25802      * Updates the tab title 
25803      * @param {String} html to set the title to.
25804      */
25805     setTitle: function(str)
25806     {
25807         if (!this.tab) {
25808             return;
25809         }
25810         this.title = str;
25811         this.tab.select('a', true).first().dom.innerHTML = str;
25812         
25813     }
25814     
25815     
25816     
25817 });
25818
25819  
25820
25821
25822  /*
25823  * - LGPL
25824  *
25825  * menu
25826  * 
25827  */
25828 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25829
25830 /**
25831  * @class Roo.bootstrap.menu.Menu
25832  * @extends Roo.bootstrap.Component
25833  * Bootstrap Menu class - container for Menu
25834  * @cfg {String} html Text of the menu
25835  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25836  * @cfg {String} icon Font awesome icon
25837  * @cfg {String} pos Menu align to (top | bottom) default bottom
25838  * 
25839  * 
25840  * @constructor
25841  * Create a new Menu
25842  * @param {Object} config The config object
25843  */
25844
25845
25846 Roo.bootstrap.menu.Menu = function(config){
25847     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25848     
25849     this.addEvents({
25850         /**
25851          * @event beforeshow
25852          * Fires before this menu is displayed
25853          * @param {Roo.bootstrap.menu.Menu} this
25854          */
25855         beforeshow : true,
25856         /**
25857          * @event beforehide
25858          * Fires before this menu is hidden
25859          * @param {Roo.bootstrap.menu.Menu} this
25860          */
25861         beforehide : true,
25862         /**
25863          * @event show
25864          * Fires after this menu is displayed
25865          * @param {Roo.bootstrap.menu.Menu} this
25866          */
25867         show : true,
25868         /**
25869          * @event hide
25870          * Fires after this menu is hidden
25871          * @param {Roo.bootstrap.menu.Menu} this
25872          */
25873         hide : true,
25874         /**
25875          * @event click
25876          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25877          * @param {Roo.bootstrap.menu.Menu} this
25878          * @param {Roo.EventObject} e
25879          */
25880         click : true
25881     });
25882     
25883 };
25884
25885 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25886     
25887     submenu : false,
25888     html : '',
25889     weight : 'default',
25890     icon : false,
25891     pos : 'bottom',
25892     
25893     
25894     getChildContainer : function() {
25895         if(this.isSubMenu){
25896             return this.el;
25897         }
25898         
25899         return this.el.select('ul.dropdown-menu', true).first();  
25900     },
25901     
25902     getAutoCreate : function()
25903     {
25904         var text = [
25905             {
25906                 tag : 'span',
25907                 cls : 'roo-menu-text',
25908                 html : this.html
25909             }
25910         ];
25911         
25912         if(this.icon){
25913             text.unshift({
25914                 tag : 'i',
25915                 cls : 'fa ' + this.icon
25916             })
25917         }
25918         
25919         
25920         var cfg = {
25921             tag : 'div',
25922             cls : 'btn-group',
25923             cn : [
25924                 {
25925                     tag : 'button',
25926                     cls : 'dropdown-button btn btn-' + this.weight,
25927                     cn : text
25928                 },
25929                 {
25930                     tag : 'button',
25931                     cls : 'dropdown-toggle btn btn-' + this.weight,
25932                     cn : [
25933                         {
25934                             tag : 'span',
25935                             cls : 'caret'
25936                         }
25937                     ]
25938                 },
25939                 {
25940                     tag : 'ul',
25941                     cls : 'dropdown-menu'
25942                 }
25943             ]
25944             
25945         };
25946         
25947         if(this.pos == 'top'){
25948             cfg.cls += ' dropup';
25949         }
25950         
25951         if(this.isSubMenu){
25952             cfg = {
25953                 tag : 'ul',
25954                 cls : 'dropdown-menu'
25955             }
25956         }
25957         
25958         return cfg;
25959     },
25960     
25961     onRender : function(ct, position)
25962     {
25963         this.isSubMenu = ct.hasClass('dropdown-submenu');
25964         
25965         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25966     },
25967     
25968     initEvents : function() 
25969     {
25970         if(this.isSubMenu){
25971             return;
25972         }
25973         
25974         this.hidden = true;
25975         
25976         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25977         this.triggerEl.on('click', this.onTriggerPress, this);
25978         
25979         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25980         this.buttonEl.on('click', this.onClick, this);
25981         
25982     },
25983     
25984     list : function()
25985     {
25986         if(this.isSubMenu){
25987             return this.el;
25988         }
25989         
25990         return this.el.select('ul.dropdown-menu', true).first();
25991     },
25992     
25993     onClick : function(e)
25994     {
25995         this.fireEvent("click", this, e);
25996     },
25997     
25998     onTriggerPress  : function(e)
25999     {   
26000         if (this.isVisible()) {
26001             this.hide();
26002         } else {
26003             this.show();
26004         }
26005     },
26006     
26007     isVisible : function(){
26008         return !this.hidden;
26009     },
26010     
26011     show : function()
26012     {
26013         this.fireEvent("beforeshow", this);
26014         
26015         this.hidden = false;
26016         this.el.addClass('open');
26017         
26018         Roo.get(document).on("mouseup", this.onMouseUp, this);
26019         
26020         this.fireEvent("show", this);
26021         
26022         
26023     },
26024     
26025     hide : function()
26026     {
26027         this.fireEvent("beforehide", this);
26028         
26029         this.hidden = true;
26030         this.el.removeClass('open');
26031         
26032         Roo.get(document).un("mouseup", this.onMouseUp);
26033         
26034         this.fireEvent("hide", this);
26035     },
26036     
26037     onMouseUp : function()
26038     {
26039         this.hide();
26040     }
26041     
26042 });
26043
26044  
26045  /*
26046  * - LGPL
26047  *
26048  * menu item
26049  * 
26050  */
26051 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26052
26053 /**
26054  * @class Roo.bootstrap.menu.Item
26055  * @extends Roo.bootstrap.Component
26056  * Bootstrap MenuItem class
26057  * @cfg {Boolean} submenu (true | false) default false
26058  * @cfg {String} html text of the item
26059  * @cfg {String} href the link
26060  * @cfg {Boolean} disable (true | false) default false
26061  * @cfg {Boolean} preventDefault (true | false) default true
26062  * @cfg {String} icon Font awesome icon
26063  * @cfg {String} pos Submenu align to (left | right) default right 
26064  * 
26065  * 
26066  * @constructor
26067  * Create a new Item
26068  * @param {Object} config The config object
26069  */
26070
26071
26072 Roo.bootstrap.menu.Item = function(config){
26073     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26074     this.addEvents({
26075         /**
26076          * @event mouseover
26077          * Fires when the mouse is hovering over this menu
26078          * @param {Roo.bootstrap.menu.Item} this
26079          * @param {Roo.EventObject} e
26080          */
26081         mouseover : true,
26082         /**
26083          * @event mouseout
26084          * Fires when the mouse exits this menu
26085          * @param {Roo.bootstrap.menu.Item} this
26086          * @param {Roo.EventObject} e
26087          */
26088         mouseout : true,
26089         // raw events
26090         /**
26091          * @event click
26092          * The raw click event for the entire grid.
26093          * @param {Roo.EventObject} e
26094          */
26095         click : true
26096     });
26097 };
26098
26099 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26100     
26101     submenu : false,
26102     href : '',
26103     html : '',
26104     preventDefault: true,
26105     disable : false,
26106     icon : false,
26107     pos : 'right',
26108     
26109     getAutoCreate : function()
26110     {
26111         var text = [
26112             {
26113                 tag : 'span',
26114                 cls : 'roo-menu-item-text',
26115                 html : this.html
26116             }
26117         ];
26118         
26119         if(this.icon){
26120             text.unshift({
26121                 tag : 'i',
26122                 cls : 'fa ' + this.icon
26123             })
26124         }
26125         
26126         var cfg = {
26127             tag : 'li',
26128             cn : [
26129                 {
26130                     tag : 'a',
26131                     href : this.href || '#',
26132                     cn : text
26133                 }
26134             ]
26135         };
26136         
26137         if(this.disable){
26138             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26139         }
26140         
26141         if(this.submenu){
26142             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26143             
26144             if(this.pos == 'left'){
26145                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26146             }
26147         }
26148         
26149         return cfg;
26150     },
26151     
26152     initEvents : function() 
26153     {
26154         this.el.on('mouseover', this.onMouseOver, this);
26155         this.el.on('mouseout', this.onMouseOut, this);
26156         
26157         this.el.select('a', true).first().on('click', this.onClick, this);
26158         
26159     },
26160     
26161     onClick : function(e)
26162     {
26163         if(this.preventDefault){
26164             e.preventDefault();
26165         }
26166         
26167         this.fireEvent("click", this, e);
26168     },
26169     
26170     onMouseOver : function(e)
26171     {
26172         if(this.submenu && this.pos == 'left'){
26173             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26174         }
26175         
26176         this.fireEvent("mouseover", this, e);
26177     },
26178     
26179     onMouseOut : function(e)
26180     {
26181         this.fireEvent("mouseout", this, e);
26182     }
26183 });
26184
26185  
26186
26187  /*
26188  * - LGPL
26189  *
26190  * menu separator
26191  * 
26192  */
26193 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26194
26195 /**
26196  * @class Roo.bootstrap.menu.Separator
26197  * @extends Roo.bootstrap.Component
26198  * Bootstrap Separator class
26199  * 
26200  * @constructor
26201  * Create a new Separator
26202  * @param {Object} config The config object
26203  */
26204
26205
26206 Roo.bootstrap.menu.Separator = function(config){
26207     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26208 };
26209
26210 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26211     
26212     getAutoCreate : function(){
26213         var cfg = {
26214             tag : 'li',
26215             cls: 'divider'
26216         };
26217         
26218         return cfg;
26219     }
26220    
26221 });
26222
26223  
26224
26225  /*
26226  * - LGPL
26227  *
26228  * Tooltip
26229  * 
26230  */
26231
26232 /**
26233  * @class Roo.bootstrap.Tooltip
26234  * Bootstrap Tooltip class
26235  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26236  * to determine which dom element triggers the tooltip.
26237  * 
26238  * It needs to add support for additional attributes like tooltip-position
26239  * 
26240  * @constructor
26241  * Create a new Toolti
26242  * @param {Object} config The config object
26243  */
26244
26245 Roo.bootstrap.Tooltip = function(config){
26246     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26247     
26248     this.alignment = Roo.bootstrap.Tooltip.alignment;
26249     
26250     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26251         this.alignment = config.alignment;
26252     }
26253     
26254 };
26255
26256 Roo.apply(Roo.bootstrap.Tooltip, {
26257     /**
26258      * @function init initialize tooltip monitoring.
26259      * @static
26260      */
26261     currentEl : false,
26262     currentTip : false,
26263     currentRegion : false,
26264     
26265     //  init : delay?
26266     
26267     init : function()
26268     {
26269         Roo.get(document).on('mouseover', this.enter ,this);
26270         Roo.get(document).on('mouseout', this.leave, this);
26271          
26272         
26273         this.currentTip = new Roo.bootstrap.Tooltip();
26274     },
26275     
26276     enter : function(ev)
26277     {
26278         var dom = ev.getTarget();
26279         
26280         //Roo.log(['enter',dom]);
26281         var el = Roo.fly(dom);
26282         if (this.currentEl) {
26283             //Roo.log(dom);
26284             //Roo.log(this.currentEl);
26285             //Roo.log(this.currentEl.contains(dom));
26286             if (this.currentEl == el) {
26287                 return;
26288             }
26289             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26290                 return;
26291             }
26292
26293         }
26294         
26295         if (this.currentTip.el) {
26296             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26297         }    
26298         //Roo.log(ev);
26299         
26300         if(!el || el.dom == document){
26301             return;
26302         }
26303         
26304         var bindEl = el;
26305         
26306         // you can not look for children, as if el is the body.. then everythign is the child..
26307         if (!el.attr('tooltip')) { //
26308             if (!el.select("[tooltip]").elements.length) {
26309                 return;
26310             }
26311             // is the mouse over this child...?
26312             bindEl = el.select("[tooltip]").first();
26313             var xy = ev.getXY();
26314             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26315                 //Roo.log("not in region.");
26316                 return;
26317             }
26318             //Roo.log("child element over..");
26319             
26320         }
26321         this.currentEl = bindEl;
26322         this.currentTip.bind(bindEl);
26323         this.currentRegion = Roo.lib.Region.getRegion(dom);
26324         this.currentTip.enter();
26325         
26326     },
26327     leave : function(ev)
26328     {
26329         var dom = ev.getTarget();
26330         //Roo.log(['leave',dom]);
26331         if (!this.currentEl) {
26332             return;
26333         }
26334         
26335         
26336         if (dom != this.currentEl.dom) {
26337             return;
26338         }
26339         var xy = ev.getXY();
26340         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26341             return;
26342         }
26343         // only activate leave if mouse cursor is outside... bounding box..
26344         
26345         
26346         
26347         
26348         if (this.currentTip) {
26349             this.currentTip.leave();
26350         }
26351         //Roo.log('clear currentEl');
26352         this.currentEl = false;
26353         
26354         
26355     },
26356     alignment : {
26357         'left' : ['r-l', [-2,0], 'right'],
26358         'right' : ['l-r', [2,0], 'left'],
26359         'bottom' : ['t-b', [0,2], 'top'],
26360         'top' : [ 'b-t', [0,-2], 'bottom']
26361     }
26362     
26363 });
26364
26365
26366 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26367     
26368     
26369     bindEl : false,
26370     
26371     delay : null, // can be { show : 300 , hide: 500}
26372     
26373     timeout : null,
26374     
26375     hoverState : null, //???
26376     
26377     placement : 'bottom', 
26378     
26379     alignment : false,
26380     
26381     getAutoCreate : function(){
26382     
26383         var cfg = {
26384            cls : 'tooltip',
26385            role : 'tooltip',
26386            cn : [
26387                 {
26388                     cls : 'tooltip-arrow'
26389                 },
26390                 {
26391                     cls : 'tooltip-inner'
26392                 }
26393            ]
26394         };
26395         
26396         return cfg;
26397     },
26398     bind : function(el)
26399     {
26400         this.bindEl = el;
26401     },
26402       
26403     
26404     enter : function () {
26405        
26406         if (this.timeout != null) {
26407             clearTimeout(this.timeout);
26408         }
26409         
26410         this.hoverState = 'in';
26411          //Roo.log("enter - show");
26412         if (!this.delay || !this.delay.show) {
26413             this.show();
26414             return;
26415         }
26416         var _t = this;
26417         this.timeout = setTimeout(function () {
26418             if (_t.hoverState == 'in') {
26419                 _t.show();
26420             }
26421         }, this.delay.show);
26422     },
26423     leave : function()
26424     {
26425         clearTimeout(this.timeout);
26426     
26427         this.hoverState = 'out';
26428          if (!this.delay || !this.delay.hide) {
26429             this.hide();
26430             return;
26431         }
26432        
26433         var _t = this;
26434         this.timeout = setTimeout(function () {
26435             //Roo.log("leave - timeout");
26436             
26437             if (_t.hoverState == 'out') {
26438                 _t.hide();
26439                 Roo.bootstrap.Tooltip.currentEl = false;
26440             }
26441         }, delay);
26442     },
26443     
26444     show : function (msg)
26445     {
26446         if (!this.el) {
26447             this.render(document.body);
26448         }
26449         // set content.
26450         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26451         
26452         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26453         
26454         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26455         
26456         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26457         
26458         var placement = typeof this.placement == 'function' ?
26459             this.placement.call(this, this.el, on_el) :
26460             this.placement;
26461             
26462         var autoToken = /\s?auto?\s?/i;
26463         var autoPlace = autoToken.test(placement);
26464         if (autoPlace) {
26465             placement = placement.replace(autoToken, '') || 'top';
26466         }
26467         
26468         //this.el.detach()
26469         //this.el.setXY([0,0]);
26470         this.el.show();
26471         //this.el.dom.style.display='block';
26472         
26473         //this.el.appendTo(on_el);
26474         
26475         var p = this.getPosition();
26476         var box = this.el.getBox();
26477         
26478         if (autoPlace) {
26479             // fixme..
26480         }
26481         
26482         var align = this.alignment[placement];
26483         
26484         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26485         
26486         if(placement == 'top' || placement == 'bottom'){
26487             if(xy[0] < 0){
26488                 placement = 'right';
26489             }
26490             
26491             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26492                 placement = 'left';
26493             }
26494             
26495             var scroll = Roo.select('body', true).first().getScroll();
26496             
26497             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26498                 placement = 'top';
26499             }
26500             
26501             align = this.alignment[placement];
26502         }
26503         
26504         this.el.alignTo(this.bindEl, align[0],align[1]);
26505         //var arrow = this.el.select('.arrow',true).first();
26506         //arrow.set(align[2], 
26507         
26508         this.el.addClass(placement);
26509         
26510         this.el.addClass('in fade');
26511         
26512         this.hoverState = null;
26513         
26514         if (this.el.hasClass('fade')) {
26515             // fade it?
26516         }
26517         
26518     },
26519     hide : function()
26520     {
26521          
26522         if (!this.el) {
26523             return;
26524         }
26525         //this.el.setXY([0,0]);
26526         this.el.removeClass('in');
26527         //this.el.hide();
26528         
26529     }
26530     
26531 });
26532  
26533
26534  /*
26535  * - LGPL
26536  *
26537  * Location Picker
26538  * 
26539  */
26540
26541 /**
26542  * @class Roo.bootstrap.LocationPicker
26543  * @extends Roo.bootstrap.Component
26544  * Bootstrap LocationPicker class
26545  * @cfg {Number} latitude Position when init default 0
26546  * @cfg {Number} longitude Position when init default 0
26547  * @cfg {Number} zoom default 15
26548  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26549  * @cfg {Boolean} mapTypeControl default false
26550  * @cfg {Boolean} disableDoubleClickZoom default false
26551  * @cfg {Boolean} scrollwheel default true
26552  * @cfg {Boolean} streetViewControl default false
26553  * @cfg {Number} radius default 0
26554  * @cfg {String} locationName
26555  * @cfg {Boolean} draggable default true
26556  * @cfg {Boolean} enableAutocomplete default false
26557  * @cfg {Boolean} enableReverseGeocode default true
26558  * @cfg {String} markerTitle
26559  * 
26560  * @constructor
26561  * Create a new LocationPicker
26562  * @param {Object} config The config object
26563  */
26564
26565
26566 Roo.bootstrap.LocationPicker = function(config){
26567     
26568     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26569     
26570     this.addEvents({
26571         /**
26572          * @event initial
26573          * Fires when the picker initialized.
26574          * @param {Roo.bootstrap.LocationPicker} this
26575          * @param {Google Location} location
26576          */
26577         initial : true,
26578         /**
26579          * @event positionchanged
26580          * Fires when the picker position changed.
26581          * @param {Roo.bootstrap.LocationPicker} this
26582          * @param {Google Location} location
26583          */
26584         positionchanged : true,
26585         /**
26586          * @event resize
26587          * Fires when the map resize.
26588          * @param {Roo.bootstrap.LocationPicker} this
26589          */
26590         resize : true,
26591         /**
26592          * @event show
26593          * Fires when the map show.
26594          * @param {Roo.bootstrap.LocationPicker} this
26595          */
26596         show : true,
26597         /**
26598          * @event hide
26599          * Fires when the map hide.
26600          * @param {Roo.bootstrap.LocationPicker} this
26601          */
26602         hide : true,
26603         /**
26604          * @event mapClick
26605          * Fires when click the map.
26606          * @param {Roo.bootstrap.LocationPicker} this
26607          * @param {Map event} e
26608          */
26609         mapClick : true,
26610         /**
26611          * @event mapRightClick
26612          * Fires when right click the map.
26613          * @param {Roo.bootstrap.LocationPicker} this
26614          * @param {Map event} e
26615          */
26616         mapRightClick : true,
26617         /**
26618          * @event markerClick
26619          * Fires when click the marker.
26620          * @param {Roo.bootstrap.LocationPicker} this
26621          * @param {Map event} e
26622          */
26623         markerClick : true,
26624         /**
26625          * @event markerRightClick
26626          * Fires when right click the marker.
26627          * @param {Roo.bootstrap.LocationPicker} this
26628          * @param {Map event} e
26629          */
26630         markerRightClick : true,
26631         /**
26632          * @event OverlayViewDraw
26633          * Fires when OverlayView Draw
26634          * @param {Roo.bootstrap.LocationPicker} this
26635          */
26636         OverlayViewDraw : true,
26637         /**
26638          * @event OverlayViewOnAdd
26639          * Fires when OverlayView Draw
26640          * @param {Roo.bootstrap.LocationPicker} this
26641          */
26642         OverlayViewOnAdd : true,
26643         /**
26644          * @event OverlayViewOnRemove
26645          * Fires when OverlayView Draw
26646          * @param {Roo.bootstrap.LocationPicker} this
26647          */
26648         OverlayViewOnRemove : true,
26649         /**
26650          * @event OverlayViewShow
26651          * Fires when OverlayView Draw
26652          * @param {Roo.bootstrap.LocationPicker} this
26653          * @param {Pixel} cpx
26654          */
26655         OverlayViewShow : true,
26656         /**
26657          * @event OverlayViewHide
26658          * Fires when OverlayView Draw
26659          * @param {Roo.bootstrap.LocationPicker} this
26660          */
26661         OverlayViewHide : true,
26662         /**
26663          * @event loadexception
26664          * Fires when load google lib failed.
26665          * @param {Roo.bootstrap.LocationPicker} this
26666          */
26667         loadexception : true
26668     });
26669         
26670 };
26671
26672 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26673     
26674     gMapContext: false,
26675     
26676     latitude: 0,
26677     longitude: 0,
26678     zoom: 15,
26679     mapTypeId: false,
26680     mapTypeControl: false,
26681     disableDoubleClickZoom: false,
26682     scrollwheel: true,
26683     streetViewControl: false,
26684     radius: 0,
26685     locationName: '',
26686     draggable: true,
26687     enableAutocomplete: false,
26688     enableReverseGeocode: true,
26689     markerTitle: '',
26690     
26691     getAutoCreate: function()
26692     {
26693
26694         var cfg = {
26695             tag: 'div',
26696             cls: 'roo-location-picker'
26697         };
26698         
26699         return cfg
26700     },
26701     
26702     initEvents: function(ct, position)
26703     {       
26704         if(!this.el.getWidth() || this.isApplied()){
26705             return;
26706         }
26707         
26708         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26709         
26710         this.initial();
26711     },
26712     
26713     initial: function()
26714     {
26715         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26716             this.fireEvent('loadexception', this);
26717             return;
26718         }
26719         
26720         if(!this.mapTypeId){
26721             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26722         }
26723         
26724         this.gMapContext = this.GMapContext();
26725         
26726         this.initOverlayView();
26727         
26728         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26729         
26730         var _this = this;
26731                 
26732         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26733             _this.setPosition(_this.gMapContext.marker.position);
26734         });
26735         
26736         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26737             _this.fireEvent('mapClick', this, event);
26738             
26739         });
26740
26741         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26742             _this.fireEvent('mapRightClick', this, event);
26743             
26744         });
26745         
26746         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26747             _this.fireEvent('markerClick', this, event);
26748             
26749         });
26750
26751         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26752             _this.fireEvent('markerRightClick', this, event);
26753             
26754         });
26755         
26756         this.setPosition(this.gMapContext.location);
26757         
26758         this.fireEvent('initial', this, this.gMapContext.location);
26759     },
26760     
26761     initOverlayView: function()
26762     {
26763         var _this = this;
26764         
26765         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26766             
26767             draw: function()
26768             {
26769                 _this.fireEvent('OverlayViewDraw', _this);
26770             },
26771             
26772             onAdd: function()
26773             {
26774                 _this.fireEvent('OverlayViewOnAdd', _this);
26775             },
26776             
26777             onRemove: function()
26778             {
26779                 _this.fireEvent('OverlayViewOnRemove', _this);
26780             },
26781             
26782             show: function(cpx)
26783             {
26784                 _this.fireEvent('OverlayViewShow', _this, cpx);
26785             },
26786             
26787             hide: function()
26788             {
26789                 _this.fireEvent('OverlayViewHide', _this);
26790             }
26791             
26792         });
26793     },
26794     
26795     fromLatLngToContainerPixel: function(event)
26796     {
26797         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26798     },
26799     
26800     isApplied: function() 
26801     {
26802         return this.getGmapContext() == false ? false : true;
26803     },
26804     
26805     getGmapContext: function() 
26806     {
26807         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26808     },
26809     
26810     GMapContext: function() 
26811     {
26812         var position = new google.maps.LatLng(this.latitude, this.longitude);
26813         
26814         var _map = new google.maps.Map(this.el.dom, {
26815             center: position,
26816             zoom: this.zoom,
26817             mapTypeId: this.mapTypeId,
26818             mapTypeControl: this.mapTypeControl,
26819             disableDoubleClickZoom: this.disableDoubleClickZoom,
26820             scrollwheel: this.scrollwheel,
26821             streetViewControl: this.streetViewControl,
26822             locationName: this.locationName,
26823             draggable: this.draggable,
26824             enableAutocomplete: this.enableAutocomplete,
26825             enableReverseGeocode: this.enableReverseGeocode
26826         });
26827         
26828         var _marker = new google.maps.Marker({
26829             position: position,
26830             map: _map,
26831             title: this.markerTitle,
26832             draggable: this.draggable
26833         });
26834         
26835         return {
26836             map: _map,
26837             marker: _marker,
26838             circle: null,
26839             location: position,
26840             radius: this.radius,
26841             locationName: this.locationName,
26842             addressComponents: {
26843                 formatted_address: null,
26844                 addressLine1: null,
26845                 addressLine2: null,
26846                 streetName: null,
26847                 streetNumber: null,
26848                 city: null,
26849                 district: null,
26850                 state: null,
26851                 stateOrProvince: null
26852             },
26853             settings: this,
26854             domContainer: this.el.dom,
26855             geodecoder: new google.maps.Geocoder()
26856         };
26857     },
26858     
26859     drawCircle: function(center, radius, options) 
26860     {
26861         if (this.gMapContext.circle != null) {
26862             this.gMapContext.circle.setMap(null);
26863         }
26864         if (radius > 0) {
26865             radius *= 1;
26866             options = Roo.apply({}, options, {
26867                 strokeColor: "#0000FF",
26868                 strokeOpacity: .35,
26869                 strokeWeight: 2,
26870                 fillColor: "#0000FF",
26871                 fillOpacity: .2
26872             });
26873             
26874             options.map = this.gMapContext.map;
26875             options.radius = radius;
26876             options.center = center;
26877             this.gMapContext.circle = new google.maps.Circle(options);
26878             return this.gMapContext.circle;
26879         }
26880         
26881         return null;
26882     },
26883     
26884     setPosition: function(location) 
26885     {
26886         this.gMapContext.location = location;
26887         this.gMapContext.marker.setPosition(location);
26888         this.gMapContext.map.panTo(location);
26889         this.drawCircle(location, this.gMapContext.radius, {});
26890         
26891         var _this = this;
26892         
26893         if (this.gMapContext.settings.enableReverseGeocode) {
26894             this.gMapContext.geodecoder.geocode({
26895                 latLng: this.gMapContext.location
26896             }, function(results, status) {
26897                 
26898                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26899                     _this.gMapContext.locationName = results[0].formatted_address;
26900                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26901                     
26902                     _this.fireEvent('positionchanged', this, location);
26903                 }
26904             });
26905             
26906             return;
26907         }
26908         
26909         this.fireEvent('positionchanged', this, location);
26910     },
26911     
26912     resize: function()
26913     {
26914         google.maps.event.trigger(this.gMapContext.map, "resize");
26915         
26916         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26917         
26918         this.fireEvent('resize', this);
26919     },
26920     
26921     setPositionByLatLng: function(latitude, longitude)
26922     {
26923         this.setPosition(new google.maps.LatLng(latitude, longitude));
26924     },
26925     
26926     getCurrentPosition: function() 
26927     {
26928         return {
26929             latitude: this.gMapContext.location.lat(),
26930             longitude: this.gMapContext.location.lng()
26931         };
26932     },
26933     
26934     getAddressName: function() 
26935     {
26936         return this.gMapContext.locationName;
26937     },
26938     
26939     getAddressComponents: function() 
26940     {
26941         return this.gMapContext.addressComponents;
26942     },
26943     
26944     address_component_from_google_geocode: function(address_components) 
26945     {
26946         var result = {};
26947         
26948         for (var i = 0; i < address_components.length; i++) {
26949             var component = address_components[i];
26950             if (component.types.indexOf("postal_code") >= 0) {
26951                 result.postalCode = component.short_name;
26952             } else if (component.types.indexOf("street_number") >= 0) {
26953                 result.streetNumber = component.short_name;
26954             } else if (component.types.indexOf("route") >= 0) {
26955                 result.streetName = component.short_name;
26956             } else if (component.types.indexOf("neighborhood") >= 0) {
26957                 result.city = component.short_name;
26958             } else if (component.types.indexOf("locality") >= 0) {
26959                 result.city = component.short_name;
26960             } else if (component.types.indexOf("sublocality") >= 0) {
26961                 result.district = component.short_name;
26962             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26963                 result.stateOrProvince = component.short_name;
26964             } else if (component.types.indexOf("country") >= 0) {
26965                 result.country = component.short_name;
26966             }
26967         }
26968         
26969         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26970         result.addressLine2 = "";
26971         return result;
26972     },
26973     
26974     setZoomLevel: function(zoom)
26975     {
26976         this.gMapContext.map.setZoom(zoom);
26977     },
26978     
26979     show: function()
26980     {
26981         if(!this.el){
26982             return;
26983         }
26984         
26985         this.el.show();
26986         
26987         this.resize();
26988         
26989         this.fireEvent('show', this);
26990     },
26991     
26992     hide: function()
26993     {
26994         if(!this.el){
26995             return;
26996         }
26997         
26998         this.el.hide();
26999         
27000         this.fireEvent('hide', this);
27001     }
27002     
27003 });
27004
27005 Roo.apply(Roo.bootstrap.LocationPicker, {
27006     
27007     OverlayView : function(map, options)
27008     {
27009         options = options || {};
27010         
27011         this.setMap(map);
27012     }
27013     
27014     
27015 });/*
27016  * - LGPL
27017  *
27018  * Alert
27019  * 
27020  */
27021
27022 /**
27023  * @class Roo.bootstrap.Alert
27024  * @extends Roo.bootstrap.Component
27025  * Bootstrap Alert class
27026  * @cfg {String} title The title of alert
27027  * @cfg {String} html The content of alert
27028  * @cfg {String} weight (  success | info | warning | danger )
27029  * @cfg {String} faicon font-awesomeicon
27030  * 
27031  * @constructor
27032  * Create a new alert
27033  * @param {Object} config The config object
27034  */
27035
27036
27037 Roo.bootstrap.Alert = function(config){
27038     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27039     
27040 };
27041
27042 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27043     
27044     title: '',
27045     html: '',
27046     weight: false,
27047     faicon: false,
27048     
27049     getAutoCreate : function()
27050     {
27051         
27052         var cfg = {
27053             tag : 'div',
27054             cls : 'alert',
27055             cn : [
27056                 {
27057                     tag : 'i',
27058                     cls : 'roo-alert-icon'
27059                     
27060                 },
27061                 {
27062                     tag : 'b',
27063                     cls : 'roo-alert-title',
27064                     html : this.title
27065                 },
27066                 {
27067                     tag : 'span',
27068                     cls : 'roo-alert-text',
27069                     html : this.html
27070                 }
27071             ]
27072         };
27073         
27074         if(this.faicon){
27075             cfg.cn[0].cls += ' fa ' + this.faicon;
27076         }
27077         
27078         if(this.weight){
27079             cfg.cls += ' alert-' + this.weight;
27080         }
27081         
27082         return cfg;
27083     },
27084     
27085     initEvents: function() 
27086     {
27087         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27088     },
27089     
27090     setTitle : function(str)
27091     {
27092         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27093     },
27094     
27095     setText : function(str)
27096     {
27097         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27098     },
27099     
27100     setWeight : function(weight)
27101     {
27102         if(this.weight){
27103             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27104         }
27105         
27106         this.weight = weight;
27107         
27108         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27109     },
27110     
27111     setIcon : function(icon)
27112     {
27113         if(this.faicon){
27114             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27115         }
27116         
27117         this.faicon = icon;
27118         
27119         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27120     },
27121     
27122     hide: function() 
27123     {
27124         this.el.hide();   
27125     },
27126     
27127     show: function() 
27128     {  
27129         this.el.show();   
27130     }
27131     
27132 });
27133
27134  
27135 /*
27136 * Licence: LGPL
27137 */
27138
27139 /**
27140  * @class Roo.bootstrap.UploadCropbox
27141  * @extends Roo.bootstrap.Component
27142  * Bootstrap UploadCropbox class
27143  * @cfg {String} emptyText show when image has been loaded
27144  * @cfg {String} rotateNotify show when image too small to rotate
27145  * @cfg {Number} errorTimeout default 3000
27146  * @cfg {Number} minWidth default 300
27147  * @cfg {Number} minHeight default 300
27148  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27149  * @cfg {Boolean} isDocument (true|false) default false
27150  * @cfg {String} url action url
27151  * @cfg {String} paramName default 'imageUpload'
27152  * @cfg {String} method default POST
27153  * @cfg {Boolean} loadMask (true|false) default true
27154  * @cfg {Boolean} loadingText default 'Loading...'
27155  * 
27156  * @constructor
27157  * Create a new UploadCropbox
27158  * @param {Object} config The config object
27159  */
27160
27161 Roo.bootstrap.UploadCropbox = function(config){
27162     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27163     
27164     this.addEvents({
27165         /**
27166          * @event beforeselectfile
27167          * Fire before select file
27168          * @param {Roo.bootstrap.UploadCropbox} this
27169          */
27170         "beforeselectfile" : true,
27171         /**
27172          * @event initial
27173          * Fire after initEvent
27174          * @param {Roo.bootstrap.UploadCropbox} this
27175          */
27176         "initial" : true,
27177         /**
27178          * @event crop
27179          * Fire after initEvent
27180          * @param {Roo.bootstrap.UploadCropbox} this
27181          * @param {String} data
27182          */
27183         "crop" : true,
27184         /**
27185          * @event prepare
27186          * Fire when preparing the file data
27187          * @param {Roo.bootstrap.UploadCropbox} this
27188          * @param {Object} file
27189          */
27190         "prepare" : true,
27191         /**
27192          * @event exception
27193          * Fire when get exception
27194          * @param {Roo.bootstrap.UploadCropbox} this
27195          * @param {XMLHttpRequest} xhr
27196          */
27197         "exception" : true,
27198         /**
27199          * @event beforeloadcanvas
27200          * Fire before load the canvas
27201          * @param {Roo.bootstrap.UploadCropbox} this
27202          * @param {String} src
27203          */
27204         "beforeloadcanvas" : true,
27205         /**
27206          * @event trash
27207          * Fire when trash image
27208          * @param {Roo.bootstrap.UploadCropbox} this
27209          */
27210         "trash" : true,
27211         /**
27212          * @event download
27213          * Fire when download the image
27214          * @param {Roo.bootstrap.UploadCropbox} this
27215          */
27216         "download" : true,
27217         /**
27218          * @event footerbuttonclick
27219          * Fire when footerbuttonclick
27220          * @param {Roo.bootstrap.UploadCropbox} this
27221          * @param {String} type
27222          */
27223         "footerbuttonclick" : true,
27224         /**
27225          * @event resize
27226          * Fire when resize
27227          * @param {Roo.bootstrap.UploadCropbox} this
27228          */
27229         "resize" : true,
27230         /**
27231          * @event rotate
27232          * Fire when rotate the image
27233          * @param {Roo.bootstrap.UploadCropbox} this
27234          * @param {String} pos
27235          */
27236         "rotate" : true,
27237         /**
27238          * @event inspect
27239          * Fire when inspect the file
27240          * @param {Roo.bootstrap.UploadCropbox} this
27241          * @param {Object} file
27242          */
27243         "inspect" : true,
27244         /**
27245          * @event upload
27246          * Fire when xhr upload the file
27247          * @param {Roo.bootstrap.UploadCropbox} this
27248          * @param {Object} data
27249          */
27250         "upload" : true,
27251         /**
27252          * @event arrange
27253          * Fire when arrange the file data
27254          * @param {Roo.bootstrap.UploadCropbox} this
27255          * @param {Object} formData
27256          */
27257         "arrange" : true
27258     });
27259     
27260     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27261 };
27262
27263 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27264     
27265     emptyText : 'Click to upload image',
27266     rotateNotify : 'Image is too small to rotate',
27267     errorTimeout : 3000,
27268     scale : 0,
27269     baseScale : 1,
27270     rotate : 0,
27271     dragable : false,
27272     pinching : false,
27273     mouseX : 0,
27274     mouseY : 0,
27275     cropData : false,
27276     minWidth : 300,
27277     minHeight : 300,
27278     file : false,
27279     exif : {},
27280     baseRotate : 1,
27281     cropType : 'image/jpeg',
27282     buttons : false,
27283     canvasLoaded : false,
27284     isDocument : false,
27285     method : 'POST',
27286     paramName : 'imageUpload',
27287     loadMask : true,
27288     loadingText : 'Loading...',
27289     maskEl : false,
27290     
27291     getAutoCreate : function()
27292     {
27293         var cfg = {
27294             tag : 'div',
27295             cls : 'roo-upload-cropbox',
27296             cn : [
27297                 {
27298                     tag : 'input',
27299                     cls : 'roo-upload-cropbox-selector',
27300                     type : 'file'
27301                 },
27302                 {
27303                     tag : 'div',
27304                     cls : 'roo-upload-cropbox-body',
27305                     style : 'cursor:pointer',
27306                     cn : [
27307                         {
27308                             tag : 'div',
27309                             cls : 'roo-upload-cropbox-preview'
27310                         },
27311                         {
27312                             tag : 'div',
27313                             cls : 'roo-upload-cropbox-thumb'
27314                         },
27315                         {
27316                             tag : 'div',
27317                             cls : 'roo-upload-cropbox-empty-notify',
27318                             html : this.emptyText
27319                         },
27320                         {
27321                             tag : 'div',
27322                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27323                             html : this.rotateNotify
27324                         }
27325                     ]
27326                 },
27327                 {
27328                     tag : 'div',
27329                     cls : 'roo-upload-cropbox-footer',
27330                     cn : {
27331                         tag : 'div',
27332                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27333                         cn : []
27334                     }
27335                 }
27336             ]
27337         };
27338         
27339         return cfg;
27340     },
27341     
27342     onRender : function(ct, position)
27343     {
27344         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27345         
27346         if (this.buttons.length) {
27347             
27348             Roo.each(this.buttons, function(bb) {
27349                 
27350                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27351                 
27352                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27353                 
27354             }, this);
27355         }
27356         
27357         if(this.loadMask){
27358             this.maskEl = this.el;
27359         }
27360     },
27361     
27362     initEvents : function()
27363     {
27364         this.urlAPI = (window.createObjectURL && window) || 
27365                                 (window.URL && URL.revokeObjectURL && URL) || 
27366                                 (window.webkitURL && webkitURL);
27367                         
27368         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27369         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27370         
27371         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27372         this.selectorEl.hide();
27373         
27374         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27375         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27376         
27377         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27378         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27379         this.thumbEl.hide();
27380         
27381         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27382         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27383         
27384         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27385         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27386         this.errorEl.hide();
27387         
27388         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27389         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27390         this.footerEl.hide();
27391         
27392         this.setThumbBoxSize();
27393         
27394         this.bind();
27395         
27396         this.resize();
27397         
27398         this.fireEvent('initial', this);
27399     },
27400
27401     bind : function()
27402     {
27403         var _this = this;
27404         
27405         window.addEventListener("resize", function() { _this.resize(); } );
27406         
27407         this.bodyEl.on('click', this.beforeSelectFile, this);
27408         
27409         if(Roo.isTouch){
27410             this.bodyEl.on('touchstart', this.onTouchStart, this);
27411             this.bodyEl.on('touchmove', this.onTouchMove, this);
27412             this.bodyEl.on('touchend', this.onTouchEnd, this);
27413         }
27414         
27415         if(!Roo.isTouch){
27416             this.bodyEl.on('mousedown', this.onMouseDown, this);
27417             this.bodyEl.on('mousemove', this.onMouseMove, this);
27418             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27419             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27420             Roo.get(document).on('mouseup', this.onMouseUp, this);
27421         }
27422         
27423         this.selectorEl.on('change', this.onFileSelected, this);
27424     },
27425     
27426     reset : function()
27427     {    
27428         this.scale = 0;
27429         this.baseScale = 1;
27430         this.rotate = 0;
27431         this.baseRotate = 1;
27432         this.dragable = false;
27433         this.pinching = false;
27434         this.mouseX = 0;
27435         this.mouseY = 0;
27436         this.cropData = false;
27437         this.notifyEl.dom.innerHTML = this.emptyText;
27438         
27439         this.selectorEl.dom.value = '';
27440         
27441     },
27442     
27443     resize : function()
27444     {
27445         if(this.fireEvent('resize', this) != false){
27446             this.setThumbBoxPosition();
27447             this.setCanvasPosition();
27448         }
27449     },
27450     
27451     onFooterButtonClick : function(e, el, o, type)
27452     {
27453         switch (type) {
27454             case 'rotate-left' :
27455                 this.onRotateLeft(e);
27456                 break;
27457             case 'rotate-right' :
27458                 this.onRotateRight(e);
27459                 break;
27460             case 'picture' :
27461                 this.beforeSelectFile(e);
27462                 break;
27463             case 'trash' :
27464                 this.trash(e);
27465                 break;
27466             case 'crop' :
27467                 this.crop(e);
27468                 break;
27469             case 'download' :
27470                 this.download(e);
27471                 break;
27472             default :
27473                 break;
27474         }
27475         
27476         this.fireEvent('footerbuttonclick', this, type);
27477     },
27478     
27479     beforeSelectFile : function(e)
27480     {
27481         e.preventDefault();
27482         
27483         if(this.fireEvent('beforeselectfile', this) != false){
27484             this.selectorEl.dom.click();
27485         }
27486     },
27487     
27488     onFileSelected : function(e)
27489     {
27490         e.preventDefault();
27491         
27492         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27493             return;
27494         }
27495         
27496         var file = this.selectorEl.dom.files[0];
27497         
27498         if(this.fireEvent('inspect', this, file) != false){
27499             this.prepare(file);
27500         }
27501         
27502     },
27503     
27504     trash : function(e)
27505     {
27506         this.fireEvent('trash', this);
27507     },
27508     
27509     download : function(e)
27510     {
27511         this.fireEvent('download', this);
27512     },
27513     
27514     loadCanvas : function(src)
27515     {   
27516         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27517             
27518             this.reset();
27519             
27520             this.imageEl = document.createElement('img');
27521             
27522             var _this = this;
27523             
27524             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27525             
27526             this.imageEl.src = src;
27527         }
27528     },
27529     
27530     onLoadCanvas : function()
27531     {   
27532         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27533         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27534         
27535         this.bodyEl.un('click', this.beforeSelectFile, this);
27536         
27537         this.notifyEl.hide();
27538         this.thumbEl.show();
27539         this.footerEl.show();
27540         
27541         this.baseRotateLevel();
27542         
27543         if(this.isDocument){
27544             this.setThumbBoxSize();
27545         }
27546         
27547         this.setThumbBoxPosition();
27548         
27549         this.baseScaleLevel();
27550         
27551         this.draw();
27552         
27553         this.resize();
27554         
27555         this.canvasLoaded = true;
27556         
27557         if(this.loadMask){
27558             this.maskEl.unmask();
27559         }
27560         
27561     },
27562     
27563     setCanvasPosition : function()
27564     {   
27565         if(!this.canvasEl){
27566             return;
27567         }
27568         
27569         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27570         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27571         
27572         this.previewEl.setLeft(pw);
27573         this.previewEl.setTop(ph);
27574         
27575     },
27576     
27577     onMouseDown : function(e)
27578     {   
27579         e.stopEvent();
27580         
27581         this.dragable = true;
27582         this.pinching = false;
27583         
27584         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27585             this.dragable = false;
27586             return;
27587         }
27588         
27589         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27590         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27591         
27592     },
27593     
27594     onMouseMove : function(e)
27595     {   
27596         e.stopEvent();
27597         
27598         if(!this.canvasLoaded){
27599             return;
27600         }
27601         
27602         if (!this.dragable){
27603             return;
27604         }
27605         
27606         var minX = Math.ceil(this.thumbEl.getLeft(true));
27607         var minY = Math.ceil(this.thumbEl.getTop(true));
27608         
27609         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27610         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27611         
27612         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27613         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27614         
27615         x = x - this.mouseX;
27616         y = y - this.mouseY;
27617         
27618         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27619         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27620         
27621         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27622         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27623         
27624         this.previewEl.setLeft(bgX);
27625         this.previewEl.setTop(bgY);
27626         
27627         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27628         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27629     },
27630     
27631     onMouseUp : function(e)
27632     {   
27633         e.stopEvent();
27634         
27635         this.dragable = false;
27636     },
27637     
27638     onMouseWheel : function(e)
27639     {   
27640         e.stopEvent();
27641         
27642         this.startScale = this.scale;
27643         
27644         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27645         
27646         if(!this.zoomable()){
27647             this.scale = this.startScale;
27648             return;
27649         }
27650         
27651         this.draw();
27652         
27653         return;
27654     },
27655     
27656     zoomable : function()
27657     {
27658         var minScale = this.thumbEl.getWidth() / this.minWidth;
27659         
27660         if(this.minWidth < this.minHeight){
27661             minScale = this.thumbEl.getHeight() / this.minHeight;
27662         }
27663         
27664         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27665         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27666         
27667         if(
27668                 this.isDocument &&
27669                 (this.rotate == 0 || this.rotate == 180) && 
27670                 (
27671                     width > this.imageEl.OriginWidth || 
27672                     height > this.imageEl.OriginHeight ||
27673                     (width < this.minWidth && height < this.minHeight)
27674                 )
27675         ){
27676             return false;
27677         }
27678         
27679         if(
27680                 this.isDocument &&
27681                 (this.rotate == 90 || this.rotate == 270) && 
27682                 (
27683                     width > this.imageEl.OriginWidth || 
27684                     height > this.imageEl.OriginHeight ||
27685                     (width < this.minHeight && height < this.minWidth)
27686                 )
27687         ){
27688             return false;
27689         }
27690         
27691         if(
27692                 !this.isDocument &&
27693                 (this.rotate == 0 || this.rotate == 180) && 
27694                 (
27695                     width < this.minWidth || 
27696                     width > this.imageEl.OriginWidth || 
27697                     height < this.minHeight || 
27698                     height > this.imageEl.OriginHeight
27699                 )
27700         ){
27701             return false;
27702         }
27703         
27704         if(
27705                 !this.isDocument &&
27706                 (this.rotate == 90 || this.rotate == 270) && 
27707                 (
27708                     width < this.minHeight || 
27709                     width > this.imageEl.OriginWidth || 
27710                     height < this.minWidth || 
27711                     height > this.imageEl.OriginHeight
27712                 )
27713         ){
27714             return false;
27715         }
27716         
27717         return true;
27718         
27719     },
27720     
27721     onRotateLeft : function(e)
27722     {   
27723         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27724             
27725             var minScale = this.thumbEl.getWidth() / this.minWidth;
27726             
27727             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27728             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27729             
27730             this.startScale = this.scale;
27731             
27732             while (this.getScaleLevel() < minScale){
27733             
27734                 this.scale = this.scale + 1;
27735                 
27736                 if(!this.zoomable()){
27737                     break;
27738                 }
27739                 
27740                 if(
27741                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27742                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27743                 ){
27744                     continue;
27745                 }
27746                 
27747                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27748
27749                 this.draw();
27750                 
27751                 return;
27752             }
27753             
27754             this.scale = this.startScale;
27755             
27756             this.onRotateFail();
27757             
27758             return false;
27759         }
27760         
27761         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27762
27763         if(this.isDocument){
27764             this.setThumbBoxSize();
27765             this.setThumbBoxPosition();
27766             this.setCanvasPosition();
27767         }
27768         
27769         this.draw();
27770         
27771         this.fireEvent('rotate', this, 'left');
27772         
27773     },
27774     
27775     onRotateRight : function(e)
27776     {
27777         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27778             
27779             var minScale = this.thumbEl.getWidth() / this.minWidth;
27780         
27781             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27782             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27783             
27784             this.startScale = this.scale;
27785             
27786             while (this.getScaleLevel() < minScale){
27787             
27788                 this.scale = this.scale + 1;
27789                 
27790                 if(!this.zoomable()){
27791                     break;
27792                 }
27793                 
27794                 if(
27795                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27796                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27797                 ){
27798                     continue;
27799                 }
27800                 
27801                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27802
27803                 this.draw();
27804                 
27805                 return;
27806             }
27807             
27808             this.scale = this.startScale;
27809             
27810             this.onRotateFail();
27811             
27812             return false;
27813         }
27814         
27815         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27816
27817         if(this.isDocument){
27818             this.setThumbBoxSize();
27819             this.setThumbBoxPosition();
27820             this.setCanvasPosition();
27821         }
27822         
27823         this.draw();
27824         
27825         this.fireEvent('rotate', this, 'right');
27826     },
27827     
27828     onRotateFail : function()
27829     {
27830         this.errorEl.show(true);
27831         
27832         var _this = this;
27833         
27834         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27835     },
27836     
27837     draw : function()
27838     {
27839         this.previewEl.dom.innerHTML = '';
27840         
27841         var canvasEl = document.createElement("canvas");
27842         
27843         var contextEl = canvasEl.getContext("2d");
27844         
27845         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27846         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27847         var center = this.imageEl.OriginWidth / 2;
27848         
27849         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27850             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27851             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27852             center = this.imageEl.OriginHeight / 2;
27853         }
27854         
27855         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27856         
27857         contextEl.translate(center, center);
27858         contextEl.rotate(this.rotate * Math.PI / 180);
27859
27860         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27861         
27862         this.canvasEl = document.createElement("canvas");
27863         
27864         this.contextEl = this.canvasEl.getContext("2d");
27865         
27866         switch (this.rotate) {
27867             case 0 :
27868                 
27869                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27870                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27871                 
27872                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27873                 
27874                 break;
27875             case 90 : 
27876                 
27877                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27878                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27879                 
27880                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27881                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27882                     break;
27883                 }
27884                 
27885                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27886                 
27887                 break;
27888             case 180 :
27889                 
27890                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27891                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27892                 
27893                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27894                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27895                     break;
27896                 }
27897                 
27898                 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);
27899                 
27900                 break;
27901             case 270 :
27902                 
27903                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27904                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27905         
27906                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27907                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27908                     break;
27909                 }
27910                 
27911                 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);
27912                 
27913                 break;
27914             default : 
27915                 break;
27916         }
27917         
27918         this.previewEl.appendChild(this.canvasEl);
27919         
27920         this.setCanvasPosition();
27921     },
27922     
27923     crop : function()
27924     {
27925         if(!this.canvasLoaded){
27926             return;
27927         }
27928         
27929         var imageCanvas = document.createElement("canvas");
27930         
27931         var imageContext = imageCanvas.getContext("2d");
27932         
27933         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27934         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27935         
27936         var center = imageCanvas.width / 2;
27937         
27938         imageContext.translate(center, center);
27939         
27940         imageContext.rotate(this.rotate * Math.PI / 180);
27941         
27942         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27943         
27944         var canvas = document.createElement("canvas");
27945         
27946         var context = canvas.getContext("2d");
27947                 
27948         canvas.width = this.minWidth;
27949         canvas.height = this.minHeight;
27950
27951         switch (this.rotate) {
27952             case 0 :
27953                 
27954                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27955                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27956                 
27957                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27958                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27959                 
27960                 var targetWidth = this.minWidth - 2 * x;
27961                 var targetHeight = this.minHeight - 2 * y;
27962                 
27963                 var scale = 1;
27964                 
27965                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27966                     scale = targetWidth / width;
27967                 }
27968                 
27969                 if(x > 0 && y == 0){
27970                     scale = targetHeight / height;
27971                 }
27972                 
27973                 if(x > 0 && y > 0){
27974                     scale = targetWidth / width;
27975                     
27976                     if(width < height){
27977                         scale = targetHeight / height;
27978                     }
27979                 }
27980                 
27981                 context.scale(scale, scale);
27982                 
27983                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27984                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27985
27986                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27987                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27988
27989                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27990                 
27991                 break;
27992             case 90 : 
27993                 
27994                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27995                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27996                 
27997                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27998                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27999                 
28000                 var targetWidth = this.minWidth - 2 * x;
28001                 var targetHeight = this.minHeight - 2 * y;
28002                 
28003                 var scale = 1;
28004                 
28005                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28006                     scale = targetWidth / width;
28007                 }
28008                 
28009                 if(x > 0 && y == 0){
28010                     scale = targetHeight / height;
28011                 }
28012                 
28013                 if(x > 0 && y > 0){
28014                     scale = targetWidth / width;
28015                     
28016                     if(width < height){
28017                         scale = targetHeight / height;
28018                     }
28019                 }
28020                 
28021                 context.scale(scale, scale);
28022                 
28023                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28024                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28025
28026                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28027                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28028                 
28029                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28030                 
28031                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28032                 
28033                 break;
28034             case 180 :
28035                 
28036                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28037                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28038                 
28039                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28040                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28041                 
28042                 var targetWidth = this.minWidth - 2 * x;
28043                 var targetHeight = this.minHeight - 2 * y;
28044                 
28045                 var scale = 1;
28046                 
28047                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28048                     scale = targetWidth / width;
28049                 }
28050                 
28051                 if(x > 0 && y == 0){
28052                     scale = targetHeight / height;
28053                 }
28054                 
28055                 if(x > 0 && y > 0){
28056                     scale = targetWidth / width;
28057                     
28058                     if(width < height){
28059                         scale = targetHeight / height;
28060                     }
28061                 }
28062                 
28063                 context.scale(scale, scale);
28064                 
28065                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28066                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28067
28068                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28069                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28070
28071                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28072                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28073                 
28074                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28075                 
28076                 break;
28077             case 270 :
28078                 
28079                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28080                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28081                 
28082                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28083                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28084                 
28085                 var targetWidth = this.minWidth - 2 * x;
28086                 var targetHeight = this.minHeight - 2 * y;
28087                 
28088                 var scale = 1;
28089                 
28090                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28091                     scale = targetWidth / width;
28092                 }
28093                 
28094                 if(x > 0 && y == 0){
28095                     scale = targetHeight / height;
28096                 }
28097                 
28098                 if(x > 0 && y > 0){
28099                     scale = targetWidth / width;
28100                     
28101                     if(width < height){
28102                         scale = targetHeight / height;
28103                     }
28104                 }
28105                 
28106                 context.scale(scale, scale);
28107                 
28108                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28109                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28110
28111                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28112                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28113                 
28114                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28115                 
28116                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28117                 
28118                 break;
28119             default : 
28120                 break;
28121         }
28122         
28123         this.cropData = canvas.toDataURL(this.cropType);
28124         
28125         if(this.fireEvent('crop', this, this.cropData) !== false){
28126             this.process(this.file, this.cropData);
28127         }
28128         
28129         return;
28130         
28131     },
28132     
28133     setThumbBoxSize : function()
28134     {
28135         var width, height;
28136         
28137         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28138             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28139             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28140             
28141             this.minWidth = width;
28142             this.minHeight = height;
28143             
28144             if(this.rotate == 90 || this.rotate == 270){
28145                 this.minWidth = height;
28146                 this.minHeight = width;
28147             }
28148         }
28149         
28150         height = 300;
28151         width = Math.ceil(this.minWidth * height / this.minHeight);
28152         
28153         if(this.minWidth > this.minHeight){
28154             width = 300;
28155             height = Math.ceil(this.minHeight * width / this.minWidth);
28156         }
28157         
28158         this.thumbEl.setStyle({
28159             width : width + 'px',
28160             height : height + 'px'
28161         });
28162
28163         return;
28164             
28165     },
28166     
28167     setThumbBoxPosition : function()
28168     {
28169         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28170         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28171         
28172         this.thumbEl.setLeft(x);
28173         this.thumbEl.setTop(y);
28174         
28175     },
28176     
28177     baseRotateLevel : function()
28178     {
28179         this.baseRotate = 1;
28180         
28181         if(
28182                 typeof(this.exif) != 'undefined' &&
28183                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28184                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28185         ){
28186             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28187         }
28188         
28189         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28190         
28191     },
28192     
28193     baseScaleLevel : function()
28194     {
28195         var width, height;
28196         
28197         if(this.isDocument){
28198             
28199             if(this.baseRotate == 6 || this.baseRotate == 8){
28200             
28201                 height = this.thumbEl.getHeight();
28202                 this.baseScale = height / this.imageEl.OriginWidth;
28203
28204                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28205                     width = this.thumbEl.getWidth();
28206                     this.baseScale = width / this.imageEl.OriginHeight;
28207                 }
28208
28209                 return;
28210             }
28211
28212             height = this.thumbEl.getHeight();
28213             this.baseScale = height / this.imageEl.OriginHeight;
28214
28215             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28216                 width = this.thumbEl.getWidth();
28217                 this.baseScale = width / this.imageEl.OriginWidth;
28218             }
28219
28220             return;
28221         }
28222         
28223         if(this.baseRotate == 6 || this.baseRotate == 8){
28224             
28225             width = this.thumbEl.getHeight();
28226             this.baseScale = width / this.imageEl.OriginHeight;
28227             
28228             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28229                 height = this.thumbEl.getWidth();
28230                 this.baseScale = height / this.imageEl.OriginHeight;
28231             }
28232             
28233             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28234                 height = this.thumbEl.getWidth();
28235                 this.baseScale = height / this.imageEl.OriginHeight;
28236                 
28237                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28238                     width = this.thumbEl.getHeight();
28239                     this.baseScale = width / this.imageEl.OriginWidth;
28240                 }
28241             }
28242             
28243             return;
28244         }
28245         
28246         width = this.thumbEl.getWidth();
28247         this.baseScale = width / this.imageEl.OriginWidth;
28248         
28249         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28250             height = this.thumbEl.getHeight();
28251             this.baseScale = height / this.imageEl.OriginHeight;
28252         }
28253         
28254         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28255             
28256             height = this.thumbEl.getHeight();
28257             this.baseScale = height / this.imageEl.OriginHeight;
28258             
28259             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28260                 width = this.thumbEl.getWidth();
28261                 this.baseScale = width / this.imageEl.OriginWidth;
28262             }
28263             
28264         }
28265         
28266         return;
28267     },
28268     
28269     getScaleLevel : function()
28270     {
28271         return this.baseScale * Math.pow(1.1, this.scale);
28272     },
28273     
28274     onTouchStart : function(e)
28275     {
28276         if(!this.canvasLoaded){
28277             this.beforeSelectFile(e);
28278             return;
28279         }
28280         
28281         var touches = e.browserEvent.touches;
28282         
28283         if(!touches){
28284             return;
28285         }
28286         
28287         if(touches.length == 1){
28288             this.onMouseDown(e);
28289             return;
28290         }
28291         
28292         if(touches.length != 2){
28293             return;
28294         }
28295         
28296         var coords = [];
28297         
28298         for(var i = 0, finger; finger = touches[i]; i++){
28299             coords.push(finger.pageX, finger.pageY);
28300         }
28301         
28302         var x = Math.pow(coords[0] - coords[2], 2);
28303         var y = Math.pow(coords[1] - coords[3], 2);
28304         
28305         this.startDistance = Math.sqrt(x + y);
28306         
28307         this.startScale = this.scale;
28308         
28309         this.pinching = true;
28310         this.dragable = false;
28311         
28312     },
28313     
28314     onTouchMove : function(e)
28315     {
28316         if(!this.pinching && !this.dragable){
28317             return;
28318         }
28319         
28320         var touches = e.browserEvent.touches;
28321         
28322         if(!touches){
28323             return;
28324         }
28325         
28326         if(this.dragable){
28327             this.onMouseMove(e);
28328             return;
28329         }
28330         
28331         var coords = [];
28332         
28333         for(var i = 0, finger; finger = touches[i]; i++){
28334             coords.push(finger.pageX, finger.pageY);
28335         }
28336         
28337         var x = Math.pow(coords[0] - coords[2], 2);
28338         var y = Math.pow(coords[1] - coords[3], 2);
28339         
28340         this.endDistance = Math.sqrt(x + y);
28341         
28342         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28343         
28344         if(!this.zoomable()){
28345             this.scale = this.startScale;
28346             return;
28347         }
28348         
28349         this.draw();
28350         
28351     },
28352     
28353     onTouchEnd : function(e)
28354     {
28355         this.pinching = false;
28356         this.dragable = false;
28357         
28358     },
28359     
28360     process : function(file, crop)
28361     {
28362         if(this.loadMask){
28363             this.maskEl.mask(this.loadingText);
28364         }
28365         
28366         this.xhr = new XMLHttpRequest();
28367         
28368         file.xhr = this.xhr;
28369
28370         this.xhr.open(this.method, this.url, true);
28371         
28372         var headers = {
28373             "Accept": "application/json",
28374             "Cache-Control": "no-cache",
28375             "X-Requested-With": "XMLHttpRequest"
28376         };
28377         
28378         for (var headerName in headers) {
28379             var headerValue = headers[headerName];
28380             if (headerValue) {
28381                 this.xhr.setRequestHeader(headerName, headerValue);
28382             }
28383         }
28384         
28385         var _this = this;
28386         
28387         this.xhr.onload = function()
28388         {
28389             _this.xhrOnLoad(_this.xhr);
28390         }
28391         
28392         this.xhr.onerror = function()
28393         {
28394             _this.xhrOnError(_this.xhr);
28395         }
28396         
28397         var formData = new FormData();
28398
28399         formData.append('returnHTML', 'NO');
28400         
28401         if(crop){
28402             formData.append('crop', crop);
28403         }
28404         
28405         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28406             formData.append(this.paramName, file, file.name);
28407         }
28408         
28409         if(typeof(file.filename) != 'undefined'){
28410             formData.append('filename', file.filename);
28411         }
28412         
28413         if(typeof(file.mimetype) != 'undefined'){
28414             formData.append('mimetype', file.mimetype);
28415         }
28416         
28417         if(this.fireEvent('arrange', this, formData) != false){
28418             this.xhr.send(formData);
28419         };
28420     },
28421     
28422     xhrOnLoad : function(xhr)
28423     {
28424         if(this.loadMask){
28425             this.maskEl.unmask();
28426         }
28427         
28428         if (xhr.readyState !== 4) {
28429             this.fireEvent('exception', this, xhr);
28430             return;
28431         }
28432
28433         var response = Roo.decode(xhr.responseText);
28434         
28435         if(!response.success){
28436             this.fireEvent('exception', this, xhr);
28437             return;
28438         }
28439         
28440         var response = Roo.decode(xhr.responseText);
28441         
28442         this.fireEvent('upload', this, response);
28443         
28444     },
28445     
28446     xhrOnError : function()
28447     {
28448         if(this.loadMask){
28449             this.maskEl.unmask();
28450         }
28451         
28452         Roo.log('xhr on error');
28453         
28454         var response = Roo.decode(xhr.responseText);
28455           
28456         Roo.log(response);
28457         
28458     },
28459     
28460     prepare : function(file)
28461     {   
28462         if(this.loadMask){
28463             this.maskEl.mask(this.loadingText);
28464         }
28465         
28466         this.file = false;
28467         this.exif = {};
28468         
28469         if(typeof(file) === 'string'){
28470             this.loadCanvas(file);
28471             return;
28472         }
28473         
28474         if(!file || !this.urlAPI){
28475             return;
28476         }
28477         
28478         this.file = file;
28479         this.cropType = file.type;
28480         
28481         var _this = this;
28482         
28483         if(this.fireEvent('prepare', this, this.file) != false){
28484             
28485             var reader = new FileReader();
28486             
28487             reader.onload = function (e) {
28488                 if (e.target.error) {
28489                     Roo.log(e.target.error);
28490                     return;
28491                 }
28492                 
28493                 var buffer = e.target.result,
28494                     dataView = new DataView(buffer),
28495                     offset = 2,
28496                     maxOffset = dataView.byteLength - 4,
28497                     markerBytes,
28498                     markerLength;
28499                 
28500                 if (dataView.getUint16(0) === 0xffd8) {
28501                     while (offset < maxOffset) {
28502                         markerBytes = dataView.getUint16(offset);
28503                         
28504                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28505                             markerLength = dataView.getUint16(offset + 2) + 2;
28506                             if (offset + markerLength > dataView.byteLength) {
28507                                 Roo.log('Invalid meta data: Invalid segment size.');
28508                                 break;
28509                             }
28510                             
28511                             if(markerBytes == 0xffe1){
28512                                 _this.parseExifData(
28513                                     dataView,
28514                                     offset,
28515                                     markerLength
28516                                 );
28517                             }
28518                             
28519                             offset += markerLength;
28520                             
28521                             continue;
28522                         }
28523                         
28524                         break;
28525                     }
28526                     
28527                 }
28528                 
28529                 var url = _this.urlAPI.createObjectURL(_this.file);
28530                 
28531                 _this.loadCanvas(url);
28532                 
28533                 return;
28534             }
28535             
28536             reader.readAsArrayBuffer(this.file);
28537             
28538         }
28539         
28540     },
28541     
28542     parseExifData : function(dataView, offset, length)
28543     {
28544         var tiffOffset = offset + 10,
28545             littleEndian,
28546             dirOffset;
28547     
28548         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28549             // No Exif data, might be XMP data instead
28550             return;
28551         }
28552         
28553         // Check for the ASCII code for "Exif" (0x45786966):
28554         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28555             // No Exif data, might be XMP data instead
28556             return;
28557         }
28558         if (tiffOffset + 8 > dataView.byteLength) {
28559             Roo.log('Invalid Exif data: Invalid segment size.');
28560             return;
28561         }
28562         // Check for the two null bytes:
28563         if (dataView.getUint16(offset + 8) !== 0x0000) {
28564             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28565             return;
28566         }
28567         // Check the byte alignment:
28568         switch (dataView.getUint16(tiffOffset)) {
28569         case 0x4949:
28570             littleEndian = true;
28571             break;
28572         case 0x4D4D:
28573             littleEndian = false;
28574             break;
28575         default:
28576             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28577             return;
28578         }
28579         // Check for the TIFF tag marker (0x002A):
28580         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28581             Roo.log('Invalid Exif data: Missing TIFF marker.');
28582             return;
28583         }
28584         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28585         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28586         
28587         this.parseExifTags(
28588             dataView,
28589             tiffOffset,
28590             tiffOffset + dirOffset,
28591             littleEndian
28592         );
28593     },
28594     
28595     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28596     {
28597         var tagsNumber,
28598             dirEndOffset,
28599             i;
28600         if (dirOffset + 6 > dataView.byteLength) {
28601             Roo.log('Invalid Exif data: Invalid directory offset.');
28602             return;
28603         }
28604         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28605         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28606         if (dirEndOffset + 4 > dataView.byteLength) {
28607             Roo.log('Invalid Exif data: Invalid directory size.');
28608             return;
28609         }
28610         for (i = 0; i < tagsNumber; i += 1) {
28611             this.parseExifTag(
28612                 dataView,
28613                 tiffOffset,
28614                 dirOffset + 2 + 12 * i, // tag offset
28615                 littleEndian
28616             );
28617         }
28618         // Return the offset to the next directory:
28619         return dataView.getUint32(dirEndOffset, littleEndian);
28620     },
28621     
28622     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28623     {
28624         var tag = dataView.getUint16(offset, littleEndian);
28625         
28626         this.exif[tag] = this.getExifValue(
28627             dataView,
28628             tiffOffset,
28629             offset,
28630             dataView.getUint16(offset + 2, littleEndian), // tag type
28631             dataView.getUint32(offset + 4, littleEndian), // tag length
28632             littleEndian
28633         );
28634     },
28635     
28636     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28637     {
28638         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28639             tagSize,
28640             dataOffset,
28641             values,
28642             i,
28643             str,
28644             c;
28645     
28646         if (!tagType) {
28647             Roo.log('Invalid Exif data: Invalid tag type.');
28648             return;
28649         }
28650         
28651         tagSize = tagType.size * length;
28652         // Determine if the value is contained in the dataOffset bytes,
28653         // or if the value at the dataOffset is a pointer to the actual data:
28654         dataOffset = tagSize > 4 ?
28655                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28656         if (dataOffset + tagSize > dataView.byteLength) {
28657             Roo.log('Invalid Exif data: Invalid data offset.');
28658             return;
28659         }
28660         if (length === 1) {
28661             return tagType.getValue(dataView, dataOffset, littleEndian);
28662         }
28663         values = [];
28664         for (i = 0; i < length; i += 1) {
28665             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28666         }
28667         
28668         if (tagType.ascii) {
28669             str = '';
28670             // Concatenate the chars:
28671             for (i = 0; i < values.length; i += 1) {
28672                 c = values[i];
28673                 // Ignore the terminating NULL byte(s):
28674                 if (c === '\u0000') {
28675                     break;
28676                 }
28677                 str += c;
28678             }
28679             return str;
28680         }
28681         return values;
28682     }
28683     
28684 });
28685
28686 Roo.apply(Roo.bootstrap.UploadCropbox, {
28687     tags : {
28688         'Orientation': 0x0112
28689     },
28690     
28691     Orientation: {
28692             1: 0, //'top-left',
28693 //            2: 'top-right',
28694             3: 180, //'bottom-right',
28695 //            4: 'bottom-left',
28696 //            5: 'left-top',
28697             6: 90, //'right-top',
28698 //            7: 'right-bottom',
28699             8: 270 //'left-bottom'
28700     },
28701     
28702     exifTagTypes : {
28703         // byte, 8-bit unsigned int:
28704         1: {
28705             getValue: function (dataView, dataOffset) {
28706                 return dataView.getUint8(dataOffset);
28707             },
28708             size: 1
28709         },
28710         // ascii, 8-bit byte:
28711         2: {
28712             getValue: function (dataView, dataOffset) {
28713                 return String.fromCharCode(dataView.getUint8(dataOffset));
28714             },
28715             size: 1,
28716             ascii: true
28717         },
28718         // short, 16 bit int:
28719         3: {
28720             getValue: function (dataView, dataOffset, littleEndian) {
28721                 return dataView.getUint16(dataOffset, littleEndian);
28722             },
28723             size: 2
28724         },
28725         // long, 32 bit int:
28726         4: {
28727             getValue: function (dataView, dataOffset, littleEndian) {
28728                 return dataView.getUint32(dataOffset, littleEndian);
28729             },
28730             size: 4
28731         },
28732         // rational = two long values, first is numerator, second is denominator:
28733         5: {
28734             getValue: function (dataView, dataOffset, littleEndian) {
28735                 return dataView.getUint32(dataOffset, littleEndian) /
28736                     dataView.getUint32(dataOffset + 4, littleEndian);
28737             },
28738             size: 8
28739         },
28740         // slong, 32 bit signed int:
28741         9: {
28742             getValue: function (dataView, dataOffset, littleEndian) {
28743                 return dataView.getInt32(dataOffset, littleEndian);
28744             },
28745             size: 4
28746         },
28747         // srational, two slongs, first is numerator, second is denominator:
28748         10: {
28749             getValue: function (dataView, dataOffset, littleEndian) {
28750                 return dataView.getInt32(dataOffset, littleEndian) /
28751                     dataView.getInt32(dataOffset + 4, littleEndian);
28752             },
28753             size: 8
28754         }
28755     },
28756     
28757     footer : {
28758         STANDARD : [
28759             {
28760                 tag : 'div',
28761                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28762                 action : 'rotate-left',
28763                 cn : [
28764                     {
28765                         tag : 'button',
28766                         cls : 'btn btn-default',
28767                         html : '<i class="fa fa-undo"></i>'
28768                     }
28769                 ]
28770             },
28771             {
28772                 tag : 'div',
28773                 cls : 'btn-group roo-upload-cropbox-picture',
28774                 action : 'picture',
28775                 cn : [
28776                     {
28777                         tag : 'button',
28778                         cls : 'btn btn-default',
28779                         html : '<i class="fa fa-picture-o"></i>'
28780                     }
28781                 ]
28782             },
28783             {
28784                 tag : 'div',
28785                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28786                 action : 'rotate-right',
28787                 cn : [
28788                     {
28789                         tag : 'button',
28790                         cls : 'btn btn-default',
28791                         html : '<i class="fa fa-repeat"></i>'
28792                     }
28793                 ]
28794             }
28795         ],
28796         DOCUMENT : [
28797             {
28798                 tag : 'div',
28799                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28800                 action : 'rotate-left',
28801                 cn : [
28802                     {
28803                         tag : 'button',
28804                         cls : 'btn btn-default',
28805                         html : '<i class="fa fa-undo"></i>'
28806                     }
28807                 ]
28808             },
28809             {
28810                 tag : 'div',
28811                 cls : 'btn-group roo-upload-cropbox-download',
28812                 action : 'download',
28813                 cn : [
28814                     {
28815                         tag : 'button',
28816                         cls : 'btn btn-default',
28817                         html : '<i class="fa fa-download"></i>'
28818                     }
28819                 ]
28820             },
28821             {
28822                 tag : 'div',
28823                 cls : 'btn-group roo-upload-cropbox-crop',
28824                 action : 'crop',
28825                 cn : [
28826                     {
28827                         tag : 'button',
28828                         cls : 'btn btn-default',
28829                         html : '<i class="fa fa-crop"></i>'
28830                     }
28831                 ]
28832             },
28833             {
28834                 tag : 'div',
28835                 cls : 'btn-group roo-upload-cropbox-trash',
28836                 action : 'trash',
28837                 cn : [
28838                     {
28839                         tag : 'button',
28840                         cls : 'btn btn-default',
28841                         html : '<i class="fa fa-trash"></i>'
28842                     }
28843                 ]
28844             },
28845             {
28846                 tag : 'div',
28847                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28848                 action : 'rotate-right',
28849                 cn : [
28850                     {
28851                         tag : 'button',
28852                         cls : 'btn btn-default',
28853                         html : '<i class="fa fa-repeat"></i>'
28854                     }
28855                 ]
28856             }
28857         ],
28858         ROTATOR : [
28859             {
28860                 tag : 'div',
28861                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28862                 action : 'rotate-left',
28863                 cn : [
28864                     {
28865                         tag : 'button',
28866                         cls : 'btn btn-default',
28867                         html : '<i class="fa fa-undo"></i>'
28868                     }
28869                 ]
28870             },
28871             {
28872                 tag : 'div',
28873                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28874                 action : 'rotate-right',
28875                 cn : [
28876                     {
28877                         tag : 'button',
28878                         cls : 'btn btn-default',
28879                         html : '<i class="fa fa-repeat"></i>'
28880                     }
28881                 ]
28882             }
28883         ]
28884     }
28885 });
28886
28887 /*
28888 * Licence: LGPL
28889 */
28890
28891 /**
28892  * @class Roo.bootstrap.DocumentManager
28893  * @extends Roo.bootstrap.Component
28894  * Bootstrap DocumentManager class
28895  * @cfg {String} paramName default 'imageUpload'
28896  * @cfg {String} toolTipName default 'filename'
28897  * @cfg {String} method default POST
28898  * @cfg {String} url action url
28899  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28900  * @cfg {Boolean} multiple multiple upload default true
28901  * @cfg {Number} thumbSize default 300
28902  * @cfg {String} fieldLabel
28903  * @cfg {Number} labelWidth default 4
28904  * @cfg {String} labelAlign (left|top) default left
28905  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28906 * @cfg {Number} labellg set the width of label (1-12)
28907  * @cfg {Number} labelmd set the width of label (1-12)
28908  * @cfg {Number} labelsm set the width of label (1-12)
28909  * @cfg {Number} labelxs set the width of label (1-12)
28910  * 
28911  * @constructor
28912  * Create a new DocumentManager
28913  * @param {Object} config The config object
28914  */
28915
28916 Roo.bootstrap.DocumentManager = function(config){
28917     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28918     
28919     this.files = [];
28920     this.delegates = [];
28921     
28922     this.addEvents({
28923         /**
28924          * @event initial
28925          * Fire when initial the DocumentManager
28926          * @param {Roo.bootstrap.DocumentManager} this
28927          */
28928         "initial" : true,
28929         /**
28930          * @event inspect
28931          * inspect selected file
28932          * @param {Roo.bootstrap.DocumentManager} this
28933          * @param {File} file
28934          */
28935         "inspect" : true,
28936         /**
28937          * @event exception
28938          * Fire when xhr load exception
28939          * @param {Roo.bootstrap.DocumentManager} this
28940          * @param {XMLHttpRequest} xhr
28941          */
28942         "exception" : true,
28943         /**
28944          * @event afterupload
28945          * Fire when xhr load exception
28946          * @param {Roo.bootstrap.DocumentManager} this
28947          * @param {XMLHttpRequest} xhr
28948          */
28949         "afterupload" : true,
28950         /**
28951          * @event prepare
28952          * prepare the form data
28953          * @param {Roo.bootstrap.DocumentManager} this
28954          * @param {Object} formData
28955          */
28956         "prepare" : true,
28957         /**
28958          * @event remove
28959          * Fire when remove the file
28960          * @param {Roo.bootstrap.DocumentManager} this
28961          * @param {Object} file
28962          */
28963         "remove" : true,
28964         /**
28965          * @event refresh
28966          * Fire after refresh the file
28967          * @param {Roo.bootstrap.DocumentManager} this
28968          */
28969         "refresh" : true,
28970         /**
28971          * @event click
28972          * Fire after click the image
28973          * @param {Roo.bootstrap.DocumentManager} this
28974          * @param {Object} file
28975          */
28976         "click" : true,
28977         /**
28978          * @event edit
28979          * Fire when upload a image and editable set to true
28980          * @param {Roo.bootstrap.DocumentManager} this
28981          * @param {Object} file
28982          */
28983         "edit" : true,
28984         /**
28985          * @event beforeselectfile
28986          * Fire before select file
28987          * @param {Roo.bootstrap.DocumentManager} this
28988          */
28989         "beforeselectfile" : true,
28990         /**
28991          * @event process
28992          * Fire before process file
28993          * @param {Roo.bootstrap.DocumentManager} this
28994          * @param {Object} file
28995          */
28996         "process" : true,
28997         /**
28998          * @event previewrendered
28999          * Fire when preview rendered
29000          * @param {Roo.bootstrap.DocumentManager} this
29001          * @param {Object} file
29002          */
29003         "previewrendered" : true,
29004         /**
29005          */
29006         "previewResize" : true
29007         
29008     });
29009 };
29010
29011 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29012     
29013     boxes : 0,
29014     inputName : '',
29015     thumbSize : 300,
29016     multiple : true,
29017     files : false,
29018     method : 'POST',
29019     url : '',
29020     paramName : 'imageUpload',
29021     toolTipName : 'filename',
29022     fieldLabel : '',
29023     labelWidth : 4,
29024     labelAlign : 'left',
29025     editable : true,
29026     delegates : false,
29027     xhr : false, 
29028     
29029     labellg : 0,
29030     labelmd : 0,
29031     labelsm : 0,
29032     labelxs : 0,
29033     
29034     getAutoCreate : function()
29035     {   
29036         var managerWidget = {
29037             tag : 'div',
29038             cls : 'roo-document-manager',
29039             cn : [
29040                 {
29041                     tag : 'input',
29042                     cls : 'roo-document-manager-selector',
29043                     type : 'file'
29044                 },
29045                 {
29046                     tag : 'div',
29047                     cls : 'roo-document-manager-uploader',
29048                     cn : [
29049                         {
29050                             tag : 'div',
29051                             cls : 'roo-document-manager-upload-btn',
29052                             html : '<i class="fa fa-plus"></i>'
29053                         }
29054                     ]
29055                     
29056                 }
29057             ]
29058         };
29059         
29060         var content = [
29061             {
29062                 tag : 'div',
29063                 cls : 'column col-md-12',
29064                 cn : managerWidget
29065             }
29066         ];
29067         
29068         if(this.fieldLabel.length){
29069             
29070             content = [
29071                 {
29072                     tag : 'div',
29073                     cls : 'column col-md-12',
29074                     html : this.fieldLabel
29075                 },
29076                 {
29077                     tag : 'div',
29078                     cls : 'column col-md-12',
29079                     cn : managerWidget
29080                 }
29081             ];
29082
29083             if(this.labelAlign == 'left'){
29084                 content = [
29085                     {
29086                         tag : 'div',
29087                         cls : 'column',
29088                         html : this.fieldLabel
29089                     },
29090                     {
29091                         tag : 'div',
29092                         cls : 'column',
29093                         cn : managerWidget
29094                     }
29095                 ];
29096                 
29097                 if(this.labelWidth > 12){
29098                     content[0].style = "width: " + this.labelWidth + 'px';
29099                 }
29100
29101                 if(this.labelWidth < 13 && this.labelmd == 0){
29102                     this.labelmd = this.labelWidth;
29103                 }
29104
29105                 if(this.labellg > 0){
29106                     content[0].cls += ' col-lg-' + this.labellg;
29107                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29108                 }
29109
29110                 if(this.labelmd > 0){
29111                     content[0].cls += ' col-md-' + this.labelmd;
29112                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29113                 }
29114
29115                 if(this.labelsm > 0){
29116                     content[0].cls += ' col-sm-' + this.labelsm;
29117                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29118                 }
29119
29120                 if(this.labelxs > 0){
29121                     content[0].cls += ' col-xs-' + this.labelxs;
29122                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29123                 }
29124                 
29125             }
29126         }
29127         
29128         var cfg = {
29129             tag : 'div',
29130             cls : 'row clearfix',
29131             cn : content
29132         };
29133         
29134         return cfg;
29135         
29136     },
29137     
29138     initEvents : function()
29139     {
29140         this.managerEl = this.el.select('.roo-document-manager', true).first();
29141         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29142         
29143         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29144         this.selectorEl.hide();
29145         
29146         if(this.multiple){
29147             this.selectorEl.attr('multiple', 'multiple');
29148         }
29149         
29150         this.selectorEl.on('change', this.onFileSelected, this);
29151         
29152         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29153         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29154         
29155         this.uploader.on('click', this.onUploaderClick, this);
29156         
29157         this.renderProgressDialog();
29158         
29159         var _this = this;
29160         
29161         window.addEventListener("resize", function() { _this.refresh(); } );
29162         
29163         this.fireEvent('initial', this);
29164     },
29165     
29166     renderProgressDialog : function()
29167     {
29168         var _this = this;
29169         
29170         this.progressDialog = new Roo.bootstrap.Modal({
29171             cls : 'roo-document-manager-progress-dialog',
29172             allow_close : false,
29173             title : '',
29174             buttons : [
29175                 {
29176                     name  :'cancel',
29177                     weight : 'danger',
29178                     html : 'Cancel'
29179                 }
29180             ], 
29181             listeners : { 
29182                 btnclick : function() {
29183                     _this.uploadCancel();
29184                     this.hide();
29185                 }
29186             }
29187         });
29188          
29189         this.progressDialog.render(Roo.get(document.body));
29190          
29191         this.progress = new Roo.bootstrap.Progress({
29192             cls : 'roo-document-manager-progress',
29193             active : true,
29194             striped : true
29195         });
29196         
29197         this.progress.render(this.progressDialog.getChildContainer());
29198         
29199         this.progressBar = new Roo.bootstrap.ProgressBar({
29200             cls : 'roo-document-manager-progress-bar',
29201             aria_valuenow : 0,
29202             aria_valuemin : 0,
29203             aria_valuemax : 12,
29204             panel : 'success'
29205         });
29206         
29207         this.progressBar.render(this.progress.getChildContainer());
29208     },
29209     
29210     onUploaderClick : function(e)
29211     {
29212         e.preventDefault();
29213      
29214         if(this.fireEvent('beforeselectfile', this) != false){
29215             this.selectorEl.dom.click();
29216         }
29217         
29218     },
29219     
29220     onFileSelected : function(e)
29221     {
29222         e.preventDefault();
29223         
29224         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29225             return;
29226         }
29227         
29228         Roo.each(this.selectorEl.dom.files, function(file){
29229             if(this.fireEvent('inspect', this, file) != false){
29230                 this.files.push(file);
29231             }
29232         }, this);
29233         
29234         this.queue();
29235         
29236     },
29237     
29238     queue : function()
29239     {
29240         this.selectorEl.dom.value = '';
29241         
29242         if(!this.files || !this.files.length){
29243             return;
29244         }
29245         
29246         if(this.boxes > 0 && this.files.length > this.boxes){
29247             this.files = this.files.slice(0, this.boxes);
29248         }
29249         
29250         this.uploader.show();
29251         
29252         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29253             this.uploader.hide();
29254         }
29255         
29256         var _this = this;
29257         
29258         var files = [];
29259         
29260         var docs = [];
29261         
29262         Roo.each(this.files, function(file){
29263             
29264             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29265                 var f = this.renderPreview(file);
29266                 files.push(f);
29267                 return;
29268             }
29269             
29270             if(file.type.indexOf('image') != -1){
29271                 this.delegates.push(
29272                     (function(){
29273                         _this.process(file);
29274                     }).createDelegate(this)
29275                 );
29276         
29277                 return;
29278             }
29279             
29280             docs.push(
29281                 (function(){
29282                     _this.process(file);
29283                 }).createDelegate(this)
29284             );
29285             
29286         }, this);
29287         
29288         this.files = files;
29289         
29290         this.delegates = this.delegates.concat(docs);
29291         
29292         if(!this.delegates.length){
29293             this.refresh();
29294             return;
29295         }
29296         
29297         this.progressBar.aria_valuemax = this.delegates.length;
29298         
29299         this.arrange();
29300         
29301         return;
29302     },
29303     
29304     arrange : function()
29305     {
29306         if(!this.delegates.length){
29307             this.progressDialog.hide();
29308             this.refresh();
29309             return;
29310         }
29311         
29312         var delegate = this.delegates.shift();
29313         
29314         this.progressDialog.show();
29315         
29316         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29317         
29318         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29319         
29320         delegate();
29321     },
29322     
29323     refresh : function()
29324     {
29325         this.uploader.show();
29326         
29327         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29328             this.uploader.hide();
29329         }
29330         
29331         Roo.isTouch ? this.closable(false) : this.closable(true);
29332         
29333         this.fireEvent('refresh', this);
29334     },
29335     
29336     onRemove : function(e, el, o)
29337     {
29338         e.preventDefault();
29339         
29340         this.fireEvent('remove', this, o);
29341         
29342     },
29343     
29344     remove : function(o)
29345     {
29346         var files = [];
29347         
29348         Roo.each(this.files, function(file){
29349             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29350                 files.push(file);
29351                 return;
29352             }
29353
29354             o.target.remove();
29355
29356         }, this);
29357         
29358         this.files = files;
29359         
29360         this.refresh();
29361     },
29362     
29363     clear : function()
29364     {
29365         Roo.each(this.files, function(file){
29366             if(!file.target){
29367                 return;
29368             }
29369             
29370             file.target.remove();
29371
29372         }, this);
29373         
29374         this.files = [];
29375         
29376         this.refresh();
29377     },
29378     
29379     onClick : function(e, el, o)
29380     {
29381         e.preventDefault();
29382         
29383         this.fireEvent('click', this, o);
29384         
29385     },
29386     
29387     closable : function(closable)
29388     {
29389         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29390             
29391             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29392             
29393             if(closable){
29394                 el.show();
29395                 return;
29396             }
29397             
29398             el.hide();
29399             
29400         }, this);
29401     },
29402     
29403     xhrOnLoad : function(xhr)
29404     {
29405         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29406             el.remove();
29407         }, this);
29408         
29409         if (xhr.readyState !== 4) {
29410             this.arrange();
29411             this.fireEvent('exception', this, xhr);
29412             return;
29413         }
29414
29415         var response = Roo.decode(xhr.responseText);
29416         
29417         if(!response.success){
29418             this.arrange();
29419             this.fireEvent('exception', this, xhr);
29420             return;
29421         }
29422         
29423         var file = this.renderPreview(response.data);
29424         
29425         this.files.push(file);
29426         
29427         this.arrange();
29428         
29429         this.fireEvent('afterupload', this, xhr);
29430         
29431     },
29432     
29433     xhrOnError : function(xhr)
29434     {
29435         Roo.log('xhr on error');
29436         
29437         var response = Roo.decode(xhr.responseText);
29438           
29439         Roo.log(response);
29440         
29441         this.arrange();
29442     },
29443     
29444     process : function(file)
29445     {
29446         if(this.fireEvent('process', this, file) !== false){
29447             if(this.editable && file.type.indexOf('image') != -1){
29448                 this.fireEvent('edit', this, file);
29449                 return;
29450             }
29451
29452             this.uploadStart(file, false);
29453
29454             return;
29455         }
29456         
29457     },
29458     
29459     uploadStart : function(file, crop)
29460     {
29461         this.xhr = new XMLHttpRequest();
29462         
29463         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29464             this.arrange();
29465             return;
29466         }
29467         
29468         file.xhr = this.xhr;
29469             
29470         this.managerEl.createChild({
29471             tag : 'div',
29472             cls : 'roo-document-manager-loading',
29473             cn : [
29474                 {
29475                     tag : 'div',
29476                     tooltip : file.name,
29477                     cls : 'roo-document-manager-thumb',
29478                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29479                 }
29480             ]
29481
29482         });
29483
29484         this.xhr.open(this.method, this.url, true);
29485         
29486         var headers = {
29487             "Accept": "application/json",
29488             "Cache-Control": "no-cache",
29489             "X-Requested-With": "XMLHttpRequest"
29490         };
29491         
29492         for (var headerName in headers) {
29493             var headerValue = headers[headerName];
29494             if (headerValue) {
29495                 this.xhr.setRequestHeader(headerName, headerValue);
29496             }
29497         }
29498         
29499         var _this = this;
29500         
29501         this.xhr.onload = function()
29502         {
29503             _this.xhrOnLoad(_this.xhr);
29504         }
29505         
29506         this.xhr.onerror = function()
29507         {
29508             _this.xhrOnError(_this.xhr);
29509         }
29510         
29511         var formData = new FormData();
29512
29513         formData.append('returnHTML', 'NO');
29514         
29515         if(crop){
29516             formData.append('crop', crop);
29517         }
29518         
29519         formData.append(this.paramName, file, file.name);
29520         
29521         var options = {
29522             file : file, 
29523             manually : false
29524         };
29525         
29526         if(this.fireEvent('prepare', this, formData, options) != false){
29527             
29528             if(options.manually){
29529                 return;
29530             }
29531             
29532             this.xhr.send(formData);
29533             return;
29534         };
29535         
29536         this.uploadCancel();
29537     },
29538     
29539     uploadCancel : function()
29540     {
29541         if (this.xhr) {
29542             this.xhr.abort();
29543         }
29544         
29545         this.delegates = [];
29546         
29547         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29548             el.remove();
29549         }, this);
29550         
29551         this.arrange();
29552     },
29553     
29554     renderPreview : function(file)
29555     {
29556         if(typeof(file.target) != 'undefined' && file.target){
29557             return file;
29558         }
29559         
29560         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29561         
29562         var previewEl = this.managerEl.createChild({
29563             tag : 'div',
29564             cls : 'roo-document-manager-preview',
29565             cn : [
29566                 {
29567                     tag : 'div',
29568                     tooltip : file[this.toolTipName],
29569                     cls : 'roo-document-manager-thumb',
29570                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29571                 },
29572                 {
29573                     tag : 'button',
29574                     cls : 'close',
29575                     html : '<i class="fa fa-times-circle"></i>'
29576                 }
29577             ]
29578         });
29579
29580         var close = previewEl.select('button.close', true).first();
29581
29582         close.on('click', this.onRemove, this, file);
29583
29584         file.target = previewEl;
29585
29586         var image = previewEl.select('img', true).first();
29587         
29588         var _this = this;
29589         
29590         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29591         
29592         image.on('click', this.onClick, this, file);
29593         
29594         this.fireEvent('previewrendered', this, file);
29595         
29596         return file;
29597         
29598     },
29599     
29600     onPreviewLoad : function(file, image)
29601     {
29602         if(typeof(file.target) == 'undefined' || !file.target){
29603             return;
29604         }
29605         
29606         var width = image.dom.naturalWidth || image.dom.width;
29607         var height = image.dom.naturalHeight || image.dom.height;
29608         
29609         if(!this.previewResize) {
29610             return;
29611         }
29612         
29613         if(width > height){
29614             file.target.addClass('wide');
29615             return;
29616         }
29617         
29618         file.target.addClass('tall');
29619         return;
29620         
29621     },
29622     
29623     uploadFromSource : function(file, crop)
29624     {
29625         this.xhr = new XMLHttpRequest();
29626         
29627         this.managerEl.createChild({
29628             tag : 'div',
29629             cls : 'roo-document-manager-loading',
29630             cn : [
29631                 {
29632                     tag : 'div',
29633                     tooltip : file.name,
29634                     cls : 'roo-document-manager-thumb',
29635                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29636                 }
29637             ]
29638
29639         });
29640
29641         this.xhr.open(this.method, this.url, true);
29642         
29643         var headers = {
29644             "Accept": "application/json",
29645             "Cache-Control": "no-cache",
29646             "X-Requested-With": "XMLHttpRequest"
29647         };
29648         
29649         for (var headerName in headers) {
29650             var headerValue = headers[headerName];
29651             if (headerValue) {
29652                 this.xhr.setRequestHeader(headerName, headerValue);
29653             }
29654         }
29655         
29656         var _this = this;
29657         
29658         this.xhr.onload = function()
29659         {
29660             _this.xhrOnLoad(_this.xhr);
29661         }
29662         
29663         this.xhr.onerror = function()
29664         {
29665             _this.xhrOnError(_this.xhr);
29666         }
29667         
29668         var formData = new FormData();
29669
29670         formData.append('returnHTML', 'NO');
29671         
29672         formData.append('crop', crop);
29673         
29674         if(typeof(file.filename) != 'undefined'){
29675             formData.append('filename', file.filename);
29676         }
29677         
29678         if(typeof(file.mimetype) != 'undefined'){
29679             formData.append('mimetype', file.mimetype);
29680         }
29681         
29682         Roo.log(formData);
29683         
29684         if(this.fireEvent('prepare', this, formData) != false){
29685             this.xhr.send(formData);
29686         };
29687     }
29688 });
29689
29690 /*
29691 * Licence: LGPL
29692 */
29693
29694 /**
29695  * @class Roo.bootstrap.DocumentViewer
29696  * @extends Roo.bootstrap.Component
29697  * Bootstrap DocumentViewer class
29698  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29699  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29700  * 
29701  * @constructor
29702  * Create a new DocumentViewer
29703  * @param {Object} config The config object
29704  */
29705
29706 Roo.bootstrap.DocumentViewer = function(config){
29707     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29708     
29709     this.addEvents({
29710         /**
29711          * @event initial
29712          * Fire after initEvent
29713          * @param {Roo.bootstrap.DocumentViewer} this
29714          */
29715         "initial" : true,
29716         /**
29717          * @event click
29718          * Fire after click
29719          * @param {Roo.bootstrap.DocumentViewer} this
29720          */
29721         "click" : true,
29722         /**
29723          * @event download
29724          * Fire after download button
29725          * @param {Roo.bootstrap.DocumentViewer} this
29726          */
29727         "download" : true,
29728         /**
29729          * @event trash
29730          * Fire after trash button
29731          * @param {Roo.bootstrap.DocumentViewer} this
29732          */
29733         "trash" : true
29734         
29735     });
29736 };
29737
29738 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29739     
29740     showDownload : true,
29741     
29742     showTrash : true,
29743     
29744     getAutoCreate : function()
29745     {
29746         var cfg = {
29747             tag : 'div',
29748             cls : 'roo-document-viewer',
29749             cn : [
29750                 {
29751                     tag : 'div',
29752                     cls : 'roo-document-viewer-body',
29753                     cn : [
29754                         {
29755                             tag : 'div',
29756                             cls : 'roo-document-viewer-thumb',
29757                             cn : [
29758                                 {
29759                                     tag : 'img',
29760                                     cls : 'roo-document-viewer-image'
29761                                 }
29762                             ]
29763                         }
29764                     ]
29765                 },
29766                 {
29767                     tag : 'div',
29768                     cls : 'roo-document-viewer-footer',
29769                     cn : {
29770                         tag : 'div',
29771                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29772                         cn : [
29773                             {
29774                                 tag : 'div',
29775                                 cls : 'btn-group roo-document-viewer-download',
29776                                 cn : [
29777                                     {
29778                                         tag : 'button',
29779                                         cls : 'btn btn-default',
29780                                         html : '<i class="fa fa-download"></i>'
29781                                     }
29782                                 ]
29783                             },
29784                             {
29785                                 tag : 'div',
29786                                 cls : 'btn-group roo-document-viewer-trash',
29787                                 cn : [
29788                                     {
29789                                         tag : 'button',
29790                                         cls : 'btn btn-default',
29791                                         html : '<i class="fa fa-trash"></i>'
29792                                     }
29793                                 ]
29794                             }
29795                         ]
29796                     }
29797                 }
29798             ]
29799         };
29800         
29801         return cfg;
29802     },
29803     
29804     initEvents : function()
29805     {
29806         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29807         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29808         
29809         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29810         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29811         
29812         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29813         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29814         
29815         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29816         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29817         
29818         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29819         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29820         
29821         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29822         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29823         
29824         this.bodyEl.on('click', this.onClick, this);
29825         this.downloadBtn.on('click', this.onDownload, this);
29826         this.trashBtn.on('click', this.onTrash, this);
29827         
29828         this.downloadBtn.hide();
29829         this.trashBtn.hide();
29830         
29831         if(this.showDownload){
29832             this.downloadBtn.show();
29833         }
29834         
29835         if(this.showTrash){
29836             this.trashBtn.show();
29837         }
29838         
29839         if(!this.showDownload && !this.showTrash) {
29840             this.footerEl.hide();
29841         }
29842         
29843     },
29844     
29845     initial : function()
29846     {
29847         this.fireEvent('initial', this);
29848         
29849     },
29850     
29851     onClick : function(e)
29852     {
29853         e.preventDefault();
29854         
29855         this.fireEvent('click', this);
29856     },
29857     
29858     onDownload : function(e)
29859     {
29860         e.preventDefault();
29861         
29862         this.fireEvent('download', this);
29863     },
29864     
29865     onTrash : function(e)
29866     {
29867         e.preventDefault();
29868         
29869         this.fireEvent('trash', this);
29870     }
29871     
29872 });
29873 /*
29874  * - LGPL
29875  *
29876  * nav progress bar
29877  * 
29878  */
29879
29880 /**
29881  * @class Roo.bootstrap.NavProgressBar
29882  * @extends Roo.bootstrap.Component
29883  * Bootstrap NavProgressBar class
29884  * 
29885  * @constructor
29886  * Create a new nav progress bar
29887  * @param {Object} config The config object
29888  */
29889
29890 Roo.bootstrap.NavProgressBar = function(config){
29891     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29892
29893     this.bullets = this.bullets || [];
29894    
29895 //    Roo.bootstrap.NavProgressBar.register(this);
29896      this.addEvents({
29897         /**
29898              * @event changed
29899              * Fires when the active item changes
29900              * @param {Roo.bootstrap.NavProgressBar} this
29901              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29902              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29903          */
29904         'changed': true
29905      });
29906     
29907 };
29908
29909 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29910     
29911     bullets : [],
29912     barItems : [],
29913     
29914     getAutoCreate : function()
29915     {
29916         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29917         
29918         cfg = {
29919             tag : 'div',
29920             cls : 'roo-navigation-bar-group',
29921             cn : [
29922                 {
29923                     tag : 'div',
29924                     cls : 'roo-navigation-top-bar'
29925                 },
29926                 {
29927                     tag : 'div',
29928                     cls : 'roo-navigation-bullets-bar',
29929                     cn : [
29930                         {
29931                             tag : 'ul',
29932                             cls : 'roo-navigation-bar'
29933                         }
29934                     ]
29935                 },
29936                 
29937                 {
29938                     tag : 'div',
29939                     cls : 'roo-navigation-bottom-bar'
29940                 }
29941             ]
29942             
29943         };
29944         
29945         return cfg;
29946         
29947     },
29948     
29949     initEvents: function() 
29950     {
29951         
29952     },
29953     
29954     onRender : function(ct, position) 
29955     {
29956         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29957         
29958         if(this.bullets.length){
29959             Roo.each(this.bullets, function(b){
29960                this.addItem(b);
29961             }, this);
29962         }
29963         
29964         this.format();
29965         
29966     },
29967     
29968     addItem : function(cfg)
29969     {
29970         var item = new Roo.bootstrap.NavProgressItem(cfg);
29971         
29972         item.parentId = this.id;
29973         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29974         
29975         if(cfg.html){
29976             var top = new Roo.bootstrap.Element({
29977                 tag : 'div',
29978                 cls : 'roo-navigation-bar-text'
29979             });
29980             
29981             var bottom = new Roo.bootstrap.Element({
29982                 tag : 'div',
29983                 cls : 'roo-navigation-bar-text'
29984             });
29985             
29986             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29987             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29988             
29989             var topText = new Roo.bootstrap.Element({
29990                 tag : 'span',
29991                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29992             });
29993             
29994             var bottomText = new Roo.bootstrap.Element({
29995                 tag : 'span',
29996                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29997             });
29998             
29999             topText.onRender(top.el, null);
30000             bottomText.onRender(bottom.el, null);
30001             
30002             item.topEl = top;
30003             item.bottomEl = bottom;
30004         }
30005         
30006         this.barItems.push(item);
30007         
30008         return item;
30009     },
30010     
30011     getActive : function()
30012     {
30013         var active = false;
30014         
30015         Roo.each(this.barItems, function(v){
30016             
30017             if (!v.isActive()) {
30018                 return;
30019             }
30020             
30021             active = v;
30022             return false;
30023             
30024         });
30025         
30026         return active;
30027     },
30028     
30029     setActiveItem : function(item)
30030     {
30031         var prev = false;
30032         
30033         Roo.each(this.barItems, function(v){
30034             if (v.rid == item.rid) {
30035                 return ;
30036             }
30037             
30038             if (v.isActive()) {
30039                 v.setActive(false);
30040                 prev = v;
30041             }
30042         });
30043
30044         item.setActive(true);
30045         
30046         this.fireEvent('changed', this, item, prev);
30047     },
30048     
30049     getBarItem: function(rid)
30050     {
30051         var ret = false;
30052         
30053         Roo.each(this.barItems, function(e) {
30054             if (e.rid != rid) {
30055                 return;
30056             }
30057             
30058             ret =  e;
30059             return false;
30060         });
30061         
30062         return ret;
30063     },
30064     
30065     indexOfItem : function(item)
30066     {
30067         var index = false;
30068         
30069         Roo.each(this.barItems, function(v, i){
30070             
30071             if (v.rid != item.rid) {
30072                 return;
30073             }
30074             
30075             index = i;
30076             return false
30077         });
30078         
30079         return index;
30080     },
30081     
30082     setActiveNext : function()
30083     {
30084         var i = this.indexOfItem(this.getActive());
30085         
30086         if (i > this.barItems.length) {
30087             return;
30088         }
30089         
30090         this.setActiveItem(this.barItems[i+1]);
30091     },
30092     
30093     setActivePrev : function()
30094     {
30095         var i = this.indexOfItem(this.getActive());
30096         
30097         if (i  < 1) {
30098             return;
30099         }
30100         
30101         this.setActiveItem(this.barItems[i-1]);
30102     },
30103     
30104     format : function()
30105     {
30106         if(!this.barItems.length){
30107             return;
30108         }
30109      
30110         var width = 100 / this.barItems.length;
30111         
30112         Roo.each(this.barItems, function(i){
30113             i.el.setStyle('width', width + '%');
30114             i.topEl.el.setStyle('width', width + '%');
30115             i.bottomEl.el.setStyle('width', width + '%');
30116         }, this);
30117         
30118     }
30119     
30120 });
30121 /*
30122  * - LGPL
30123  *
30124  * Nav Progress Item
30125  * 
30126  */
30127
30128 /**
30129  * @class Roo.bootstrap.NavProgressItem
30130  * @extends Roo.bootstrap.Component
30131  * Bootstrap NavProgressItem class
30132  * @cfg {String} rid the reference id
30133  * @cfg {Boolean} active (true|false) Is item active default false
30134  * @cfg {Boolean} disabled (true|false) Is item active default false
30135  * @cfg {String} html
30136  * @cfg {String} position (top|bottom) text position default bottom
30137  * @cfg {String} icon show icon instead of number
30138  * 
30139  * @constructor
30140  * Create a new NavProgressItem
30141  * @param {Object} config The config object
30142  */
30143 Roo.bootstrap.NavProgressItem = function(config){
30144     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30145     this.addEvents({
30146         // raw events
30147         /**
30148          * @event click
30149          * The raw click event for the entire grid.
30150          * @param {Roo.bootstrap.NavProgressItem} this
30151          * @param {Roo.EventObject} e
30152          */
30153         "click" : true
30154     });
30155    
30156 };
30157
30158 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30159     
30160     rid : '',
30161     active : false,
30162     disabled : false,
30163     html : '',
30164     position : 'bottom',
30165     icon : false,
30166     
30167     getAutoCreate : function()
30168     {
30169         var iconCls = 'roo-navigation-bar-item-icon';
30170         
30171         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30172         
30173         var cfg = {
30174             tag: 'li',
30175             cls: 'roo-navigation-bar-item',
30176             cn : [
30177                 {
30178                     tag : 'i',
30179                     cls : iconCls
30180                 }
30181             ]
30182         };
30183         
30184         if(this.active){
30185             cfg.cls += ' active';
30186         }
30187         if(this.disabled){
30188             cfg.cls += ' disabled';
30189         }
30190         
30191         return cfg;
30192     },
30193     
30194     disable : function()
30195     {
30196         this.setDisabled(true);
30197     },
30198     
30199     enable : function()
30200     {
30201         this.setDisabled(false);
30202     },
30203     
30204     initEvents: function() 
30205     {
30206         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30207         
30208         this.iconEl.on('click', this.onClick, this);
30209     },
30210     
30211     onClick : function(e)
30212     {
30213         e.preventDefault();
30214         
30215         if(this.disabled){
30216             return;
30217         }
30218         
30219         if(this.fireEvent('click', this, e) === false){
30220             return;
30221         };
30222         
30223         this.parent().setActiveItem(this);
30224     },
30225     
30226     isActive: function () 
30227     {
30228         return this.active;
30229     },
30230     
30231     setActive : function(state)
30232     {
30233         if(this.active == state){
30234             return;
30235         }
30236         
30237         this.active = state;
30238         
30239         if (state) {
30240             this.el.addClass('active');
30241             return;
30242         }
30243         
30244         this.el.removeClass('active');
30245         
30246         return;
30247     },
30248     
30249     setDisabled : function(state)
30250     {
30251         if(this.disabled == state){
30252             return;
30253         }
30254         
30255         this.disabled = state;
30256         
30257         if (state) {
30258             this.el.addClass('disabled');
30259             return;
30260         }
30261         
30262         this.el.removeClass('disabled');
30263     },
30264     
30265     tooltipEl : function()
30266     {
30267         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30268     }
30269 });
30270  
30271
30272  /*
30273  * - LGPL
30274  *
30275  * FieldLabel
30276  * 
30277  */
30278
30279 /**
30280  * @class Roo.bootstrap.FieldLabel
30281  * @extends Roo.bootstrap.Component
30282  * Bootstrap FieldLabel class
30283  * @cfg {String} html contents of the element
30284  * @cfg {String} tag tag of the element default label
30285  * @cfg {String} cls class of the element
30286  * @cfg {String} target label target 
30287  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30288  * @cfg {String} invalidClass default "text-warning"
30289  * @cfg {String} validClass default "text-success"
30290  * @cfg {String} iconTooltip default "This field is required"
30291  * @cfg {String} indicatorpos (left|right) default left
30292  * 
30293  * @constructor
30294  * Create a new FieldLabel
30295  * @param {Object} config The config object
30296  */
30297
30298 Roo.bootstrap.FieldLabel = function(config){
30299     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30300     
30301     this.addEvents({
30302             /**
30303              * @event invalid
30304              * Fires after the field has been marked as invalid.
30305              * @param {Roo.form.FieldLabel} this
30306              * @param {String} msg The validation message
30307              */
30308             invalid : true,
30309             /**
30310              * @event valid
30311              * Fires after the field has been validated with no errors.
30312              * @param {Roo.form.FieldLabel} this
30313              */
30314             valid : true
30315         });
30316 };
30317
30318 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30319     
30320     tag: 'label',
30321     cls: '',
30322     html: '',
30323     target: '',
30324     allowBlank : true,
30325     invalidClass : 'has-warning',
30326     validClass : 'has-success',
30327     iconTooltip : 'This field is required',
30328     indicatorpos : 'left',
30329     
30330     getAutoCreate : function(){
30331         
30332         var cls = "";
30333         if (!this.allowBlank) {
30334             cls  = "visible";
30335         }
30336         
30337         var cfg = {
30338             tag : this.tag,
30339             cls : 'roo-bootstrap-field-label ' + this.cls,
30340             for : this.target,
30341             cn : [
30342                 {
30343                     tag : 'i',
30344                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30345                     tooltip : this.iconTooltip
30346                 },
30347                 {
30348                     tag : 'span',
30349                     html : this.html
30350                 }
30351             ] 
30352         };
30353         
30354         if(this.indicatorpos == 'right'){
30355             var cfg = {
30356                 tag : this.tag,
30357                 cls : 'roo-bootstrap-field-label ' + this.cls,
30358                 for : this.target,
30359                 cn : [
30360                     {
30361                         tag : 'span',
30362                         html : this.html
30363                     },
30364                     {
30365                         tag : 'i',
30366                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30367                         tooltip : this.iconTooltip
30368                     }
30369                 ] 
30370             };
30371         }
30372         
30373         return cfg;
30374     },
30375     
30376     initEvents: function() 
30377     {
30378         Roo.bootstrap.Element.superclass.initEvents.call(this);
30379         
30380         this.indicator = this.indicatorEl();
30381         
30382         if(this.indicator){
30383             this.indicator.removeClass('visible');
30384             this.indicator.addClass('invisible');
30385         }
30386         
30387         Roo.bootstrap.FieldLabel.register(this);
30388     },
30389     
30390     indicatorEl : function()
30391     {
30392         var indicator = this.el.select('i.roo-required-indicator',true).first();
30393         
30394         if(!indicator){
30395             return false;
30396         }
30397         
30398         return indicator;
30399         
30400     },
30401     
30402     /**
30403      * Mark this field as valid
30404      */
30405     markValid : function()
30406     {
30407         if(this.indicator){
30408             this.indicator.removeClass('visible');
30409             this.indicator.addClass('invisible');
30410         }
30411         
30412         this.el.removeClass(this.invalidClass);
30413         
30414         this.el.addClass(this.validClass);
30415         
30416         this.fireEvent('valid', this);
30417     },
30418     
30419     /**
30420      * Mark this field as invalid
30421      * @param {String} msg The validation message
30422      */
30423     markInvalid : function(msg)
30424     {
30425         if(this.indicator){
30426             this.indicator.removeClass('invisible');
30427             this.indicator.addClass('visible');
30428         }
30429         
30430         this.el.removeClass(this.validClass);
30431         
30432         this.el.addClass(this.invalidClass);
30433         
30434         this.fireEvent('invalid', this, msg);
30435     }
30436     
30437    
30438 });
30439
30440 Roo.apply(Roo.bootstrap.FieldLabel, {
30441     
30442     groups: {},
30443     
30444      /**
30445     * register a FieldLabel Group
30446     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30447     */
30448     register : function(label)
30449     {
30450         if(this.groups.hasOwnProperty(label.target)){
30451             return;
30452         }
30453      
30454         this.groups[label.target] = label;
30455         
30456     },
30457     /**
30458     * fetch a FieldLabel Group based on the target
30459     * @param {string} target
30460     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30461     */
30462     get: function(target) {
30463         if (typeof(this.groups[target]) == 'undefined') {
30464             return false;
30465         }
30466         
30467         return this.groups[target] ;
30468     }
30469 });
30470
30471  
30472
30473  /*
30474  * - LGPL
30475  *
30476  * page DateSplitField.
30477  * 
30478  */
30479
30480
30481 /**
30482  * @class Roo.bootstrap.DateSplitField
30483  * @extends Roo.bootstrap.Component
30484  * Bootstrap DateSplitField class
30485  * @cfg {string} fieldLabel - the label associated
30486  * @cfg {Number} labelWidth set the width of label (0-12)
30487  * @cfg {String} labelAlign (top|left)
30488  * @cfg {Boolean} dayAllowBlank (true|false) default false
30489  * @cfg {Boolean} monthAllowBlank (true|false) default false
30490  * @cfg {Boolean} yearAllowBlank (true|false) default false
30491  * @cfg {string} dayPlaceholder 
30492  * @cfg {string} monthPlaceholder
30493  * @cfg {string} yearPlaceholder
30494  * @cfg {string} dayFormat default 'd'
30495  * @cfg {string} monthFormat default 'm'
30496  * @cfg {string} yearFormat default 'Y'
30497  * @cfg {Number} labellg set the width of label (1-12)
30498  * @cfg {Number} labelmd set the width of label (1-12)
30499  * @cfg {Number} labelsm set the width of label (1-12)
30500  * @cfg {Number} labelxs set the width of label (1-12)
30501
30502  *     
30503  * @constructor
30504  * Create a new DateSplitField
30505  * @param {Object} config The config object
30506  */
30507
30508 Roo.bootstrap.DateSplitField = function(config){
30509     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30510     
30511     this.addEvents({
30512         // raw events
30513          /**
30514          * @event years
30515          * getting the data of years
30516          * @param {Roo.bootstrap.DateSplitField} this
30517          * @param {Object} years
30518          */
30519         "years" : true,
30520         /**
30521          * @event days
30522          * getting the data of days
30523          * @param {Roo.bootstrap.DateSplitField} this
30524          * @param {Object} days
30525          */
30526         "days" : true,
30527         /**
30528          * @event invalid
30529          * Fires after the field has been marked as invalid.
30530          * @param {Roo.form.Field} this
30531          * @param {String} msg The validation message
30532          */
30533         invalid : true,
30534        /**
30535          * @event valid
30536          * Fires after the field has been validated with no errors.
30537          * @param {Roo.form.Field} this
30538          */
30539         valid : true
30540     });
30541 };
30542
30543 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30544     
30545     fieldLabel : '',
30546     labelAlign : 'top',
30547     labelWidth : 3,
30548     dayAllowBlank : false,
30549     monthAllowBlank : false,
30550     yearAllowBlank : false,
30551     dayPlaceholder : '',
30552     monthPlaceholder : '',
30553     yearPlaceholder : '',
30554     dayFormat : 'd',
30555     monthFormat : 'm',
30556     yearFormat : 'Y',
30557     isFormField : true,
30558     labellg : 0,
30559     labelmd : 0,
30560     labelsm : 0,
30561     labelxs : 0,
30562     
30563     getAutoCreate : function()
30564     {
30565         var cfg = {
30566             tag : 'div',
30567             cls : 'row roo-date-split-field-group',
30568             cn : [
30569                 {
30570                     tag : 'input',
30571                     type : 'hidden',
30572                     cls : 'form-hidden-field roo-date-split-field-group-value',
30573                     name : this.name
30574                 }
30575             ]
30576         };
30577         
30578         var labelCls = 'col-md-12';
30579         var contentCls = 'col-md-4';
30580         
30581         if(this.fieldLabel){
30582             
30583             var label = {
30584                 tag : 'div',
30585                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30586                 cn : [
30587                     {
30588                         tag : 'label',
30589                         html : this.fieldLabel
30590                     }
30591                 ]
30592             };
30593             
30594             if(this.labelAlign == 'left'){
30595             
30596                 if(this.labelWidth > 12){
30597                     label.style = "width: " + this.labelWidth + 'px';
30598                 }
30599
30600                 if(this.labelWidth < 13 && this.labelmd == 0){
30601                     this.labelmd = this.labelWidth;
30602                 }
30603
30604                 if(this.labellg > 0){
30605                     labelCls = ' col-lg-' + this.labellg;
30606                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30607                 }
30608
30609                 if(this.labelmd > 0){
30610                     labelCls = ' col-md-' + this.labelmd;
30611                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30612                 }
30613
30614                 if(this.labelsm > 0){
30615                     labelCls = ' col-sm-' + this.labelsm;
30616                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30617                 }
30618
30619                 if(this.labelxs > 0){
30620                     labelCls = ' col-xs-' + this.labelxs;
30621                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30622                 }
30623             }
30624             
30625             label.cls += ' ' + labelCls;
30626             
30627             cfg.cn.push(label);
30628         }
30629         
30630         Roo.each(['day', 'month', 'year'], function(t){
30631             cfg.cn.push({
30632                 tag : 'div',
30633                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30634             });
30635         }, this);
30636         
30637         return cfg;
30638     },
30639     
30640     inputEl: function ()
30641     {
30642         return this.el.select('.roo-date-split-field-group-value', true).first();
30643     },
30644     
30645     onRender : function(ct, position) 
30646     {
30647         var _this = this;
30648         
30649         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30650         
30651         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30652         
30653         this.dayField = new Roo.bootstrap.ComboBox({
30654             allowBlank : this.dayAllowBlank,
30655             alwaysQuery : true,
30656             displayField : 'value',
30657             editable : false,
30658             fieldLabel : '',
30659             forceSelection : true,
30660             mode : 'local',
30661             placeholder : this.dayPlaceholder,
30662             selectOnFocus : true,
30663             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30664             triggerAction : 'all',
30665             typeAhead : true,
30666             valueField : 'value',
30667             store : new Roo.data.SimpleStore({
30668                 data : (function() {    
30669                     var days = [];
30670                     _this.fireEvent('days', _this, days);
30671                     return days;
30672                 })(),
30673                 fields : [ 'value' ]
30674             }),
30675             listeners : {
30676                 select : function (_self, record, index)
30677                 {
30678                     _this.setValue(_this.getValue());
30679                 }
30680             }
30681         });
30682
30683         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30684         
30685         this.monthField = new Roo.bootstrap.MonthField({
30686             after : '<i class=\"fa fa-calendar\"></i>',
30687             allowBlank : this.monthAllowBlank,
30688             placeholder : this.monthPlaceholder,
30689             readOnly : true,
30690             listeners : {
30691                 render : function (_self)
30692                 {
30693                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30694                         e.preventDefault();
30695                         _self.focus();
30696                     });
30697                 },
30698                 select : function (_self, oldvalue, newvalue)
30699                 {
30700                     _this.setValue(_this.getValue());
30701                 }
30702             }
30703         });
30704         
30705         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30706         
30707         this.yearField = new Roo.bootstrap.ComboBox({
30708             allowBlank : this.yearAllowBlank,
30709             alwaysQuery : true,
30710             displayField : 'value',
30711             editable : false,
30712             fieldLabel : '',
30713             forceSelection : true,
30714             mode : 'local',
30715             placeholder : this.yearPlaceholder,
30716             selectOnFocus : true,
30717             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30718             triggerAction : 'all',
30719             typeAhead : true,
30720             valueField : 'value',
30721             store : new Roo.data.SimpleStore({
30722                 data : (function() {
30723                     var years = [];
30724                     _this.fireEvent('years', _this, years);
30725                     return years;
30726                 })(),
30727                 fields : [ 'value' ]
30728             }),
30729             listeners : {
30730                 select : function (_self, record, index)
30731                 {
30732                     _this.setValue(_this.getValue());
30733                 }
30734             }
30735         });
30736
30737         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30738     },
30739     
30740     setValue : function(v, format)
30741     {
30742         this.inputEl.dom.value = v;
30743         
30744         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30745         
30746         var d = Date.parseDate(v, f);
30747         
30748         if(!d){
30749             this.validate();
30750             return;
30751         }
30752         
30753         this.setDay(d.format(this.dayFormat));
30754         this.setMonth(d.format(this.monthFormat));
30755         this.setYear(d.format(this.yearFormat));
30756         
30757         this.validate();
30758         
30759         return;
30760     },
30761     
30762     setDay : function(v)
30763     {
30764         this.dayField.setValue(v);
30765         this.inputEl.dom.value = this.getValue();
30766         this.validate();
30767         return;
30768     },
30769     
30770     setMonth : function(v)
30771     {
30772         this.monthField.setValue(v, true);
30773         this.inputEl.dom.value = this.getValue();
30774         this.validate();
30775         return;
30776     },
30777     
30778     setYear : function(v)
30779     {
30780         this.yearField.setValue(v);
30781         this.inputEl.dom.value = this.getValue();
30782         this.validate();
30783         return;
30784     },
30785     
30786     getDay : function()
30787     {
30788         return this.dayField.getValue();
30789     },
30790     
30791     getMonth : function()
30792     {
30793         return this.monthField.getValue();
30794     },
30795     
30796     getYear : function()
30797     {
30798         return this.yearField.getValue();
30799     },
30800     
30801     getValue : function()
30802     {
30803         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30804         
30805         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30806         
30807         return date;
30808     },
30809     
30810     reset : function()
30811     {
30812         this.setDay('');
30813         this.setMonth('');
30814         this.setYear('');
30815         this.inputEl.dom.value = '';
30816         this.validate();
30817         return;
30818     },
30819     
30820     validate : function()
30821     {
30822         var d = this.dayField.validate();
30823         var m = this.monthField.validate();
30824         var y = this.yearField.validate();
30825         
30826         var valid = true;
30827         
30828         if(
30829                 (!this.dayAllowBlank && !d) ||
30830                 (!this.monthAllowBlank && !m) ||
30831                 (!this.yearAllowBlank && !y)
30832         ){
30833             valid = false;
30834         }
30835         
30836         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30837             return valid;
30838         }
30839         
30840         if(valid){
30841             this.markValid();
30842             return valid;
30843         }
30844         
30845         this.markInvalid();
30846         
30847         return valid;
30848     },
30849     
30850     markValid : function()
30851     {
30852         
30853         var label = this.el.select('label', true).first();
30854         var icon = this.el.select('i.fa-star', true).first();
30855
30856         if(label && icon){
30857             icon.remove();
30858         }
30859         
30860         this.fireEvent('valid', this);
30861     },
30862     
30863      /**
30864      * Mark this field as invalid
30865      * @param {String} msg The validation message
30866      */
30867     markInvalid : function(msg)
30868     {
30869         
30870         var label = this.el.select('label', true).first();
30871         var icon = this.el.select('i.fa-star', true).first();
30872
30873         if(label && !icon){
30874             this.el.select('.roo-date-split-field-label', true).createChild({
30875                 tag : 'i',
30876                 cls : 'text-danger fa fa-lg fa-star',
30877                 tooltip : 'This field is required',
30878                 style : 'margin-right:5px;'
30879             }, label, true);
30880         }
30881         
30882         this.fireEvent('invalid', this, msg);
30883     },
30884     
30885     clearInvalid : function()
30886     {
30887         var label = this.el.select('label', true).first();
30888         var icon = this.el.select('i.fa-star', true).first();
30889
30890         if(label && icon){
30891             icon.remove();
30892         }
30893         
30894         this.fireEvent('valid', this);
30895     },
30896     
30897     getName: function()
30898     {
30899         return this.name;
30900     }
30901     
30902 });
30903
30904  /**
30905  *
30906  * This is based on 
30907  * http://masonry.desandro.com
30908  *
30909  * The idea is to render all the bricks based on vertical width...
30910  *
30911  * The original code extends 'outlayer' - we might need to use that....
30912  * 
30913  */
30914
30915
30916 /**
30917  * @class Roo.bootstrap.LayoutMasonry
30918  * @extends Roo.bootstrap.Component
30919  * Bootstrap Layout Masonry class
30920  * 
30921  * @constructor
30922  * Create a new Element
30923  * @param {Object} config The config object
30924  */
30925
30926 Roo.bootstrap.LayoutMasonry = function(config){
30927     
30928     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30929     
30930     this.bricks = [];
30931     
30932     Roo.bootstrap.LayoutMasonry.register(this);
30933     
30934     this.addEvents({
30935         // raw events
30936         /**
30937          * @event layout
30938          * Fire after layout the items
30939          * @param {Roo.bootstrap.LayoutMasonry} this
30940          * @param {Roo.EventObject} e
30941          */
30942         "layout" : true
30943     });
30944     
30945 };
30946
30947 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30948     
30949     /**
30950      * @cfg {Boolean} isLayoutInstant = no animation?
30951      */   
30952     isLayoutInstant : false, // needed?
30953    
30954     /**
30955      * @cfg {Number} boxWidth  width of the columns
30956      */   
30957     boxWidth : 450,
30958     
30959       /**
30960      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30961      */   
30962     boxHeight : 0,
30963     
30964     /**
30965      * @cfg {Number} padWidth padding below box..
30966      */   
30967     padWidth : 10, 
30968     
30969     /**
30970      * @cfg {Number} gutter gutter width..
30971      */   
30972     gutter : 10,
30973     
30974      /**
30975      * @cfg {Number} maxCols maximum number of columns
30976      */   
30977     
30978     maxCols: 0,
30979     
30980     /**
30981      * @cfg {Boolean} isAutoInitial defalut true
30982      */   
30983     isAutoInitial : true, 
30984     
30985     containerWidth: 0,
30986     
30987     /**
30988      * @cfg {Boolean} isHorizontal defalut false
30989      */   
30990     isHorizontal : false, 
30991
30992     currentSize : null,
30993     
30994     tag: 'div',
30995     
30996     cls: '',
30997     
30998     bricks: null, //CompositeElement
30999     
31000     cols : 1,
31001     
31002     _isLayoutInited : false,
31003     
31004 //    isAlternative : false, // only use for vertical layout...
31005     
31006     /**
31007      * @cfg {Number} alternativePadWidth padding below box..
31008      */   
31009     alternativePadWidth : 50,
31010     
31011     selectedBrick : [],
31012     
31013     getAutoCreate : function(){
31014         
31015         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31016         
31017         var cfg = {
31018             tag: this.tag,
31019             cls: 'blog-masonary-wrapper ' + this.cls,
31020             cn : {
31021                 cls : 'mas-boxes masonary'
31022             }
31023         };
31024         
31025         return cfg;
31026     },
31027     
31028     getChildContainer: function( )
31029     {
31030         if (this.boxesEl) {
31031             return this.boxesEl;
31032         }
31033         
31034         this.boxesEl = this.el.select('.mas-boxes').first();
31035         
31036         return this.boxesEl;
31037     },
31038     
31039     
31040     initEvents : function()
31041     {
31042         var _this = this;
31043         
31044         if(this.isAutoInitial){
31045             Roo.log('hook children rendered');
31046             this.on('childrenrendered', function() {
31047                 Roo.log('children rendered');
31048                 _this.initial();
31049             } ,this);
31050         }
31051     },
31052     
31053     initial : function()
31054     {
31055         this.selectedBrick = [];
31056         
31057         this.currentSize = this.el.getBox(true);
31058         
31059         Roo.EventManager.onWindowResize(this.resize, this); 
31060
31061         if(!this.isAutoInitial){
31062             this.layout();
31063             return;
31064         }
31065         
31066         this.layout();
31067         
31068         return;
31069         //this.layout.defer(500,this);
31070         
31071     },
31072     
31073     resize : function()
31074     {
31075         var cs = this.el.getBox(true);
31076         
31077         if (
31078                 this.currentSize.width == cs.width && 
31079                 this.currentSize.x == cs.x && 
31080                 this.currentSize.height == cs.height && 
31081                 this.currentSize.y == cs.y 
31082         ) {
31083             Roo.log("no change in with or X or Y");
31084             return;
31085         }
31086         
31087         this.currentSize = cs;
31088         
31089         this.layout();
31090         
31091     },
31092     
31093     layout : function()
31094     {   
31095         this._resetLayout();
31096         
31097         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31098         
31099         this.layoutItems( isInstant );
31100       
31101         this._isLayoutInited = true;
31102         
31103         this.fireEvent('layout', this);
31104         
31105     },
31106     
31107     _resetLayout : function()
31108     {
31109         if(this.isHorizontal){
31110             this.horizontalMeasureColumns();
31111             return;
31112         }
31113         
31114         this.verticalMeasureColumns();
31115         
31116     },
31117     
31118     verticalMeasureColumns : function()
31119     {
31120         this.getContainerWidth();
31121         
31122 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31123 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31124 //            return;
31125 //        }
31126         
31127         var boxWidth = this.boxWidth + this.padWidth;
31128         
31129         if(this.containerWidth < this.boxWidth){
31130             boxWidth = this.containerWidth
31131         }
31132         
31133         var containerWidth = this.containerWidth;
31134         
31135         var cols = Math.floor(containerWidth / boxWidth);
31136         
31137         this.cols = Math.max( cols, 1 );
31138         
31139         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31140         
31141         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31142         
31143         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31144         
31145         this.colWidth = boxWidth + avail - this.padWidth;
31146         
31147         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31148         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31149     },
31150     
31151     horizontalMeasureColumns : function()
31152     {
31153         this.getContainerWidth();
31154         
31155         var boxWidth = this.boxWidth;
31156         
31157         if(this.containerWidth < boxWidth){
31158             boxWidth = this.containerWidth;
31159         }
31160         
31161         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31162         
31163         this.el.setHeight(boxWidth);
31164         
31165     },
31166     
31167     getContainerWidth : function()
31168     {
31169         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31170     },
31171     
31172     layoutItems : function( isInstant )
31173     {
31174         Roo.log(this.bricks);
31175         
31176         var items = Roo.apply([], this.bricks);
31177         
31178         if(this.isHorizontal){
31179             this._horizontalLayoutItems( items , isInstant );
31180             return;
31181         }
31182         
31183 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31184 //            this._verticalAlternativeLayoutItems( items , isInstant );
31185 //            return;
31186 //        }
31187         
31188         this._verticalLayoutItems( items , isInstant );
31189         
31190     },
31191     
31192     _verticalLayoutItems : function ( items , isInstant)
31193     {
31194         if ( !items || !items.length ) {
31195             return;
31196         }
31197         
31198         var standard = [
31199             ['xs', 'xs', 'xs', 'tall'],
31200             ['xs', 'xs', 'tall'],
31201             ['xs', 'xs', 'sm'],
31202             ['xs', 'xs', 'xs'],
31203             ['xs', 'tall'],
31204             ['xs', 'sm'],
31205             ['xs', 'xs'],
31206             ['xs'],
31207             
31208             ['sm', 'xs', 'xs'],
31209             ['sm', 'xs'],
31210             ['sm'],
31211             
31212             ['tall', 'xs', 'xs', 'xs'],
31213             ['tall', 'xs', 'xs'],
31214             ['tall', 'xs'],
31215             ['tall']
31216             
31217         ];
31218         
31219         var queue = [];
31220         
31221         var boxes = [];
31222         
31223         var box = [];
31224         
31225         Roo.each(items, function(item, k){
31226             
31227             switch (item.size) {
31228                 // these layouts take up a full box,
31229                 case 'md' :
31230                 case 'md-left' :
31231                 case 'md-right' :
31232                 case 'wide' :
31233                     
31234                     if(box.length){
31235                         boxes.push(box);
31236                         box = [];
31237                     }
31238                     
31239                     boxes.push([item]);
31240                     
31241                     break;
31242                     
31243                 case 'xs' :
31244                 case 'sm' :
31245                 case 'tall' :
31246                     
31247                     box.push(item);
31248                     
31249                     break;
31250                 default :
31251                     break;
31252                     
31253             }
31254             
31255         }, this);
31256         
31257         if(box.length){
31258             boxes.push(box);
31259             box = [];
31260         }
31261         
31262         var filterPattern = function(box, length)
31263         {
31264             if(!box.length){
31265                 return;
31266             }
31267             
31268             var match = false;
31269             
31270             var pattern = box.slice(0, length);
31271             
31272             var format = [];
31273             
31274             Roo.each(pattern, function(i){
31275                 format.push(i.size);
31276             }, this);
31277             
31278             Roo.each(standard, function(s){
31279                 
31280                 if(String(s) != String(format)){
31281                     return;
31282                 }
31283                 
31284                 match = true;
31285                 return false;
31286                 
31287             }, this);
31288             
31289             if(!match && length == 1){
31290                 return;
31291             }
31292             
31293             if(!match){
31294                 filterPattern(box, length - 1);
31295                 return;
31296             }
31297                 
31298             queue.push(pattern);
31299
31300             box = box.slice(length, box.length);
31301
31302             filterPattern(box, 4);
31303
31304             return;
31305             
31306         }
31307         
31308         Roo.each(boxes, function(box, k){
31309             
31310             if(!box.length){
31311                 return;
31312             }
31313             
31314             if(box.length == 1){
31315                 queue.push(box);
31316                 return;
31317             }
31318             
31319             filterPattern(box, 4);
31320             
31321         }, this);
31322         
31323         this._processVerticalLayoutQueue( queue, isInstant );
31324         
31325     },
31326     
31327 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31328 //    {
31329 //        if ( !items || !items.length ) {
31330 //            return;
31331 //        }
31332 //
31333 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31334 //        
31335 //    },
31336     
31337     _horizontalLayoutItems : function ( items , isInstant)
31338     {
31339         if ( !items || !items.length || items.length < 3) {
31340             return;
31341         }
31342         
31343         items.reverse();
31344         
31345         var eItems = items.slice(0, 3);
31346         
31347         items = items.slice(3, items.length);
31348         
31349         var standard = [
31350             ['xs', 'xs', 'xs', 'wide'],
31351             ['xs', 'xs', 'wide'],
31352             ['xs', 'xs', 'sm'],
31353             ['xs', 'xs', 'xs'],
31354             ['xs', 'wide'],
31355             ['xs', 'sm'],
31356             ['xs', 'xs'],
31357             ['xs'],
31358             
31359             ['sm', 'xs', 'xs'],
31360             ['sm', 'xs'],
31361             ['sm'],
31362             
31363             ['wide', 'xs', 'xs', 'xs'],
31364             ['wide', 'xs', 'xs'],
31365             ['wide', 'xs'],
31366             ['wide'],
31367             
31368             ['wide-thin']
31369         ];
31370         
31371         var queue = [];
31372         
31373         var boxes = [];
31374         
31375         var box = [];
31376         
31377         Roo.each(items, function(item, k){
31378             
31379             switch (item.size) {
31380                 case 'md' :
31381                 case 'md-left' :
31382                 case 'md-right' :
31383                 case 'tall' :
31384                     
31385                     if(box.length){
31386                         boxes.push(box);
31387                         box = [];
31388                     }
31389                     
31390                     boxes.push([item]);
31391                     
31392                     break;
31393                     
31394                 case 'xs' :
31395                 case 'sm' :
31396                 case 'wide' :
31397                 case 'wide-thin' :
31398                     
31399                     box.push(item);
31400                     
31401                     break;
31402                 default :
31403                     break;
31404                     
31405             }
31406             
31407         }, this);
31408         
31409         if(box.length){
31410             boxes.push(box);
31411             box = [];
31412         }
31413         
31414         var filterPattern = function(box, length)
31415         {
31416             if(!box.length){
31417                 return;
31418             }
31419             
31420             var match = false;
31421             
31422             var pattern = box.slice(0, length);
31423             
31424             var format = [];
31425             
31426             Roo.each(pattern, function(i){
31427                 format.push(i.size);
31428             }, this);
31429             
31430             Roo.each(standard, function(s){
31431                 
31432                 if(String(s) != String(format)){
31433                     return;
31434                 }
31435                 
31436                 match = true;
31437                 return false;
31438                 
31439             }, this);
31440             
31441             if(!match && length == 1){
31442                 return;
31443             }
31444             
31445             if(!match){
31446                 filterPattern(box, length - 1);
31447                 return;
31448             }
31449                 
31450             queue.push(pattern);
31451
31452             box = box.slice(length, box.length);
31453
31454             filterPattern(box, 4);
31455
31456             return;
31457             
31458         }
31459         
31460         Roo.each(boxes, function(box, k){
31461             
31462             if(!box.length){
31463                 return;
31464             }
31465             
31466             if(box.length == 1){
31467                 queue.push(box);
31468                 return;
31469             }
31470             
31471             filterPattern(box, 4);
31472             
31473         }, this);
31474         
31475         
31476         var prune = [];
31477         
31478         var pos = this.el.getBox(true);
31479         
31480         var minX = pos.x;
31481         
31482         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31483         
31484         var hit_end = false;
31485         
31486         Roo.each(queue, function(box){
31487             
31488             if(hit_end){
31489                 
31490                 Roo.each(box, function(b){
31491                 
31492                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31493                     b.el.hide();
31494
31495                 }, this);
31496
31497                 return;
31498             }
31499             
31500             var mx = 0;
31501             
31502             Roo.each(box, function(b){
31503                 
31504                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31505                 b.el.show();
31506
31507                 mx = Math.max(mx, b.x);
31508                 
31509             }, this);
31510             
31511             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31512             
31513             if(maxX < minX){
31514                 
31515                 Roo.each(box, function(b){
31516                 
31517                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31518                     b.el.hide();
31519                     
31520                 }, this);
31521                 
31522                 hit_end = true;
31523                 
31524                 return;
31525             }
31526             
31527             prune.push(box);
31528             
31529         }, this);
31530         
31531         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31532     },
31533     
31534     /** Sets position of item in DOM
31535     * @param {Element} item
31536     * @param {Number} x - horizontal position
31537     * @param {Number} y - vertical position
31538     * @param {Boolean} isInstant - disables transitions
31539     */
31540     _processVerticalLayoutQueue : function( queue, isInstant )
31541     {
31542         var pos = this.el.getBox(true);
31543         var x = pos.x;
31544         var y = pos.y;
31545         var maxY = [];
31546         
31547         for (var i = 0; i < this.cols; i++){
31548             maxY[i] = pos.y;
31549         }
31550         
31551         Roo.each(queue, function(box, k){
31552             
31553             var col = k % this.cols;
31554             
31555             Roo.each(box, function(b,kk){
31556                 
31557                 b.el.position('absolute');
31558                 
31559                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31560                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31561                 
31562                 if(b.size == 'md-left' || b.size == 'md-right'){
31563                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31564                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31565                 }
31566                 
31567                 b.el.setWidth(width);
31568                 b.el.setHeight(height);
31569                 // iframe?
31570                 b.el.select('iframe',true).setSize(width,height);
31571                 
31572             }, this);
31573             
31574             for (var i = 0; i < this.cols; i++){
31575                 
31576                 if(maxY[i] < maxY[col]){
31577                     col = i;
31578                     continue;
31579                 }
31580                 
31581                 col = Math.min(col, i);
31582                 
31583             }
31584             
31585             x = pos.x + col * (this.colWidth + this.padWidth);
31586             
31587             y = maxY[col];
31588             
31589             var positions = [];
31590             
31591             switch (box.length){
31592                 case 1 :
31593                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31594                     break;
31595                 case 2 :
31596                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31597                     break;
31598                 case 3 :
31599                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31600                     break;
31601                 case 4 :
31602                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31603                     break;
31604                 default :
31605                     break;
31606             }
31607             
31608             Roo.each(box, function(b,kk){
31609                 
31610                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31611                 
31612                 var sz = b.el.getSize();
31613                 
31614                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31615                 
31616             }, this);
31617             
31618         }, this);
31619         
31620         var mY = 0;
31621         
31622         for (var i = 0; i < this.cols; i++){
31623             mY = Math.max(mY, maxY[i]);
31624         }
31625         
31626         this.el.setHeight(mY - pos.y);
31627         
31628     },
31629     
31630 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31631 //    {
31632 //        var pos = this.el.getBox(true);
31633 //        var x = pos.x;
31634 //        var y = pos.y;
31635 //        var maxX = pos.right;
31636 //        
31637 //        var maxHeight = 0;
31638 //        
31639 //        Roo.each(items, function(item, k){
31640 //            
31641 //            var c = k % 2;
31642 //            
31643 //            item.el.position('absolute');
31644 //                
31645 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31646 //
31647 //            item.el.setWidth(width);
31648 //
31649 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31650 //
31651 //            item.el.setHeight(height);
31652 //            
31653 //            if(c == 0){
31654 //                item.el.setXY([x, y], isInstant ? false : true);
31655 //            } else {
31656 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31657 //            }
31658 //            
31659 //            y = y + height + this.alternativePadWidth;
31660 //            
31661 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31662 //            
31663 //        }, this);
31664 //        
31665 //        this.el.setHeight(maxHeight);
31666 //        
31667 //    },
31668     
31669     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31670     {
31671         var pos = this.el.getBox(true);
31672         
31673         var minX = pos.x;
31674         var minY = pos.y;
31675         
31676         var maxX = pos.right;
31677         
31678         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31679         
31680         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31681         
31682         Roo.each(queue, function(box, k){
31683             
31684             Roo.each(box, function(b, kk){
31685                 
31686                 b.el.position('absolute');
31687                 
31688                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31689                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31690                 
31691                 if(b.size == 'md-left' || b.size == 'md-right'){
31692                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31693                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31694                 }
31695                 
31696                 b.el.setWidth(width);
31697                 b.el.setHeight(height);
31698                 
31699             }, this);
31700             
31701             if(!box.length){
31702                 return;
31703             }
31704             
31705             var positions = [];
31706             
31707             switch (box.length){
31708                 case 1 :
31709                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31710                     break;
31711                 case 2 :
31712                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31713                     break;
31714                 case 3 :
31715                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31716                     break;
31717                 case 4 :
31718                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31719                     break;
31720                 default :
31721                     break;
31722             }
31723             
31724             Roo.each(box, function(b,kk){
31725                 
31726                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31727                 
31728                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31729                 
31730             }, this);
31731             
31732         }, this);
31733         
31734     },
31735     
31736     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31737     {
31738         Roo.each(eItems, function(b,k){
31739             
31740             b.size = (k == 0) ? 'sm' : 'xs';
31741             b.x = (k == 0) ? 2 : 1;
31742             b.y = (k == 0) ? 2 : 1;
31743             
31744             b.el.position('absolute');
31745             
31746             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31747                 
31748             b.el.setWidth(width);
31749             
31750             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31751             
31752             b.el.setHeight(height);
31753             
31754         }, this);
31755
31756         var positions = [];
31757         
31758         positions.push({
31759             x : maxX - this.unitWidth * 2 - this.gutter,
31760             y : minY
31761         });
31762         
31763         positions.push({
31764             x : maxX - this.unitWidth,
31765             y : minY + (this.unitWidth + this.gutter) * 2
31766         });
31767         
31768         positions.push({
31769             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31770             y : minY
31771         });
31772         
31773         Roo.each(eItems, function(b,k){
31774             
31775             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31776
31777         }, this);
31778         
31779     },
31780     
31781     getVerticalOneBoxColPositions : function(x, y, box)
31782     {
31783         var pos = [];
31784         
31785         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31786         
31787         if(box[0].size == 'md-left'){
31788             rand = 0;
31789         }
31790         
31791         if(box[0].size == 'md-right'){
31792             rand = 1;
31793         }
31794         
31795         pos.push({
31796             x : x + (this.unitWidth + this.gutter) * rand,
31797             y : y
31798         });
31799         
31800         return pos;
31801     },
31802     
31803     getVerticalTwoBoxColPositions : function(x, y, box)
31804     {
31805         var pos = [];
31806         
31807         if(box[0].size == 'xs'){
31808             
31809             pos.push({
31810                 x : x,
31811                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31812             });
31813
31814             pos.push({
31815                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31816                 y : y
31817             });
31818             
31819             return pos;
31820             
31821         }
31822         
31823         pos.push({
31824             x : x,
31825             y : y
31826         });
31827
31828         pos.push({
31829             x : x + (this.unitWidth + this.gutter) * 2,
31830             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31831         });
31832         
31833         return pos;
31834         
31835     },
31836     
31837     getVerticalThreeBoxColPositions : function(x, y, box)
31838     {
31839         var pos = [];
31840         
31841         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31842             
31843             pos.push({
31844                 x : x,
31845                 y : y
31846             });
31847
31848             pos.push({
31849                 x : x + (this.unitWidth + this.gutter) * 1,
31850                 y : y
31851             });
31852             
31853             pos.push({
31854                 x : x + (this.unitWidth + this.gutter) * 2,
31855                 y : y
31856             });
31857             
31858             return pos;
31859             
31860         }
31861         
31862         if(box[0].size == 'xs' && box[1].size == 'xs'){
31863             
31864             pos.push({
31865                 x : x,
31866                 y : y
31867             });
31868
31869             pos.push({
31870                 x : x,
31871                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31872             });
31873             
31874             pos.push({
31875                 x : x + (this.unitWidth + this.gutter) * 1,
31876                 y : y
31877             });
31878             
31879             return pos;
31880             
31881         }
31882         
31883         pos.push({
31884             x : x,
31885             y : y
31886         });
31887
31888         pos.push({
31889             x : x + (this.unitWidth + this.gutter) * 2,
31890             y : y
31891         });
31892
31893         pos.push({
31894             x : x + (this.unitWidth + this.gutter) * 2,
31895             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31896         });
31897             
31898         return pos;
31899         
31900     },
31901     
31902     getVerticalFourBoxColPositions : function(x, y, box)
31903     {
31904         var pos = [];
31905         
31906         if(box[0].size == 'xs'){
31907             
31908             pos.push({
31909                 x : x,
31910                 y : y
31911             });
31912
31913             pos.push({
31914                 x : x,
31915                 y : y + (this.unitHeight + this.gutter) * 1
31916             });
31917             
31918             pos.push({
31919                 x : x,
31920                 y : y + (this.unitHeight + this.gutter) * 2
31921             });
31922             
31923             pos.push({
31924                 x : x + (this.unitWidth + this.gutter) * 1,
31925                 y : y
31926             });
31927             
31928             return pos;
31929             
31930         }
31931         
31932         pos.push({
31933             x : x,
31934             y : y
31935         });
31936
31937         pos.push({
31938             x : x + (this.unitWidth + this.gutter) * 2,
31939             y : y
31940         });
31941
31942         pos.push({
31943             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31944             y : y + (this.unitHeight + this.gutter) * 1
31945         });
31946
31947         pos.push({
31948             x : x + (this.unitWidth + this.gutter) * 2,
31949             y : y + (this.unitWidth + this.gutter) * 2
31950         });
31951
31952         return pos;
31953         
31954     },
31955     
31956     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31957     {
31958         var pos = [];
31959         
31960         if(box[0].size == 'md-left'){
31961             pos.push({
31962                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31963                 y : minY
31964             });
31965             
31966             return pos;
31967         }
31968         
31969         if(box[0].size == 'md-right'){
31970             pos.push({
31971                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31972                 y : minY + (this.unitWidth + this.gutter) * 1
31973             });
31974             
31975             return pos;
31976         }
31977         
31978         var rand = Math.floor(Math.random() * (4 - box[0].y));
31979         
31980         pos.push({
31981             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31982             y : minY + (this.unitWidth + this.gutter) * rand
31983         });
31984         
31985         return pos;
31986         
31987     },
31988     
31989     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31990     {
31991         var pos = [];
31992         
31993         if(box[0].size == 'xs'){
31994             
31995             pos.push({
31996                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31997                 y : minY
31998             });
31999
32000             pos.push({
32001                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32003             });
32004             
32005             return pos;
32006             
32007         }
32008         
32009         pos.push({
32010             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32011             y : minY
32012         });
32013
32014         pos.push({
32015             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32016             y : minY + (this.unitWidth + this.gutter) * 2
32017         });
32018         
32019         return pos;
32020         
32021     },
32022     
32023     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32024     {
32025         var pos = [];
32026         
32027         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32028             
32029             pos.push({
32030                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32031                 y : minY
32032             });
32033
32034             pos.push({
32035                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32036                 y : minY + (this.unitWidth + this.gutter) * 1
32037             });
32038             
32039             pos.push({
32040                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32041                 y : minY + (this.unitWidth + this.gutter) * 2
32042             });
32043             
32044             return pos;
32045             
32046         }
32047         
32048         if(box[0].size == 'xs' && box[1].size == 'xs'){
32049             
32050             pos.push({
32051                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32052                 y : minY
32053             });
32054
32055             pos.push({
32056                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32057                 y : minY
32058             });
32059             
32060             pos.push({
32061                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32062                 y : minY + (this.unitWidth + this.gutter) * 1
32063             });
32064             
32065             return pos;
32066             
32067         }
32068         
32069         pos.push({
32070             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32071             y : minY
32072         });
32073
32074         pos.push({
32075             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32076             y : minY + (this.unitWidth + this.gutter) * 2
32077         });
32078
32079         pos.push({
32080             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32081             y : minY + (this.unitWidth + this.gutter) * 2
32082         });
32083             
32084         return pos;
32085         
32086     },
32087     
32088     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32089     {
32090         var pos = [];
32091         
32092         if(box[0].size == 'xs'){
32093             
32094             pos.push({
32095                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32096                 y : minY
32097             });
32098
32099             pos.push({
32100                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32101                 y : minY
32102             });
32103             
32104             pos.push({
32105                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32106                 y : minY
32107             });
32108             
32109             pos.push({
32110                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32111                 y : minY + (this.unitWidth + this.gutter) * 1
32112             });
32113             
32114             return pos;
32115             
32116         }
32117         
32118         pos.push({
32119             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32120             y : minY
32121         });
32122         
32123         pos.push({
32124             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32125             y : minY + (this.unitWidth + this.gutter) * 2
32126         });
32127         
32128         pos.push({
32129             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32130             y : minY + (this.unitWidth + this.gutter) * 2
32131         });
32132         
32133         pos.push({
32134             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32135             y : minY + (this.unitWidth + this.gutter) * 2
32136         });
32137
32138         return pos;
32139         
32140     },
32141     
32142     /**
32143     * remove a Masonry Brick
32144     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32145     */
32146     removeBrick : function(brick_id)
32147     {
32148         if (!brick_id) {
32149             return;
32150         }
32151         
32152         for (var i = 0; i<this.bricks.length; i++) {
32153             if (this.bricks[i].id == brick_id) {
32154                 this.bricks.splice(i,1);
32155                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32156                 this.initial();
32157             }
32158         }
32159     },
32160     
32161     /**
32162     * adds a Masonry Brick
32163     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32164     */
32165     addBrick : function(cfg)
32166     {
32167         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32168         //this.register(cn);
32169         cn.parentId = this.id;
32170         cn.render(this.el);
32171         return cn;
32172     },
32173     
32174     /**
32175     * register a Masonry Brick
32176     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32177     */
32178     
32179     register : function(brick)
32180     {
32181         this.bricks.push(brick);
32182         brick.masonryId = this.id;
32183     },
32184     
32185     /**
32186     * clear all the Masonry Brick
32187     */
32188     clearAll : function()
32189     {
32190         this.bricks = [];
32191         //this.getChildContainer().dom.innerHTML = "";
32192         this.el.dom.innerHTML = '';
32193     },
32194     
32195     getSelected : function()
32196     {
32197         if (!this.selectedBrick) {
32198             return false;
32199         }
32200         
32201         return this.selectedBrick;
32202     }
32203 });
32204
32205 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32206     
32207     groups: {},
32208      /**
32209     * register a Masonry Layout
32210     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32211     */
32212     
32213     register : function(layout)
32214     {
32215         this.groups[layout.id] = layout;
32216     },
32217     /**
32218     * fetch a  Masonry Layout based on the masonry layout ID
32219     * @param {string} the masonry layout to add
32220     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32221     */
32222     
32223     get: function(layout_id) {
32224         if (typeof(this.groups[layout_id]) == 'undefined') {
32225             return false;
32226         }
32227         return this.groups[layout_id] ;
32228     }
32229     
32230     
32231     
32232 });
32233
32234  
32235
32236  /**
32237  *
32238  * This is based on 
32239  * http://masonry.desandro.com
32240  *
32241  * The idea is to render all the bricks based on vertical width...
32242  *
32243  * The original code extends 'outlayer' - we might need to use that....
32244  * 
32245  */
32246
32247
32248 /**
32249  * @class Roo.bootstrap.LayoutMasonryAuto
32250  * @extends Roo.bootstrap.Component
32251  * Bootstrap Layout Masonry class
32252  * 
32253  * @constructor
32254  * Create a new Element
32255  * @param {Object} config The config object
32256  */
32257
32258 Roo.bootstrap.LayoutMasonryAuto = function(config){
32259     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32260 };
32261
32262 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32263     
32264       /**
32265      * @cfg {Boolean} isFitWidth  - resize the width..
32266      */   
32267     isFitWidth : false,  // options..
32268     /**
32269      * @cfg {Boolean} isOriginLeft = left align?
32270      */   
32271     isOriginLeft : true,
32272     /**
32273      * @cfg {Boolean} isOriginTop = top align?
32274      */   
32275     isOriginTop : false,
32276     /**
32277      * @cfg {Boolean} isLayoutInstant = no animation?
32278      */   
32279     isLayoutInstant : false, // needed?
32280     /**
32281      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32282      */   
32283     isResizingContainer : true,
32284     /**
32285      * @cfg {Number} columnWidth  width of the columns 
32286      */   
32287     
32288     columnWidth : 0,
32289     
32290     /**
32291      * @cfg {Number} maxCols maximum number of columns
32292      */   
32293     
32294     maxCols: 0,
32295     /**
32296      * @cfg {Number} padHeight padding below box..
32297      */   
32298     
32299     padHeight : 10, 
32300     
32301     /**
32302      * @cfg {Boolean} isAutoInitial defalut true
32303      */   
32304     
32305     isAutoInitial : true, 
32306     
32307     // private?
32308     gutter : 0,
32309     
32310     containerWidth: 0,
32311     initialColumnWidth : 0,
32312     currentSize : null,
32313     
32314     colYs : null, // array.
32315     maxY : 0,
32316     padWidth: 10,
32317     
32318     
32319     tag: 'div',
32320     cls: '',
32321     bricks: null, //CompositeElement
32322     cols : 0, // array?
32323     // element : null, // wrapped now this.el
32324     _isLayoutInited : null, 
32325     
32326     
32327     getAutoCreate : function(){
32328         
32329         var cfg = {
32330             tag: this.tag,
32331             cls: 'blog-masonary-wrapper ' + this.cls,
32332             cn : {
32333                 cls : 'mas-boxes masonary'
32334             }
32335         };
32336         
32337         return cfg;
32338     },
32339     
32340     getChildContainer: function( )
32341     {
32342         if (this.boxesEl) {
32343             return this.boxesEl;
32344         }
32345         
32346         this.boxesEl = this.el.select('.mas-boxes').first();
32347         
32348         return this.boxesEl;
32349     },
32350     
32351     
32352     initEvents : function()
32353     {
32354         var _this = this;
32355         
32356         if(this.isAutoInitial){
32357             Roo.log('hook children rendered');
32358             this.on('childrenrendered', function() {
32359                 Roo.log('children rendered');
32360                 _this.initial();
32361             } ,this);
32362         }
32363         
32364     },
32365     
32366     initial : function()
32367     {
32368         this.reloadItems();
32369
32370         this.currentSize = this.el.getBox(true);
32371
32372         /// was window resize... - let's see if this works..
32373         Roo.EventManager.onWindowResize(this.resize, this); 
32374
32375         if(!this.isAutoInitial){
32376             this.layout();
32377             return;
32378         }
32379         
32380         this.layout.defer(500,this);
32381     },
32382     
32383     reloadItems: function()
32384     {
32385         this.bricks = this.el.select('.masonry-brick', true);
32386         
32387         this.bricks.each(function(b) {
32388             //Roo.log(b.getSize());
32389             if (!b.attr('originalwidth')) {
32390                 b.attr('originalwidth',  b.getSize().width);
32391             }
32392             
32393         });
32394         
32395         Roo.log(this.bricks.elements.length);
32396     },
32397     
32398     resize : function()
32399     {
32400         Roo.log('resize');
32401         var cs = this.el.getBox(true);
32402         
32403         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32404             Roo.log("no change in with or X");
32405             return;
32406         }
32407         this.currentSize = cs;
32408         this.layout();
32409     },
32410     
32411     layout : function()
32412     {
32413          Roo.log('layout');
32414         this._resetLayout();
32415         //this._manageStamps();
32416       
32417         // don't animate first layout
32418         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32419         this.layoutItems( isInstant );
32420       
32421         // flag for initalized
32422         this._isLayoutInited = true;
32423     },
32424     
32425     layoutItems : function( isInstant )
32426     {
32427         //var items = this._getItemsForLayout( this.items );
32428         // original code supports filtering layout items.. we just ignore it..
32429         
32430         this._layoutItems( this.bricks , isInstant );
32431       
32432         this._postLayout();
32433     },
32434     _layoutItems : function ( items , isInstant)
32435     {
32436        //this.fireEvent( 'layout', this, items );
32437     
32438
32439         if ( !items || !items.elements.length ) {
32440           // no items, emit event with empty array
32441             return;
32442         }
32443
32444         var queue = [];
32445         items.each(function(item) {
32446             Roo.log("layout item");
32447             Roo.log(item);
32448             // get x/y object from method
32449             var position = this._getItemLayoutPosition( item );
32450             // enqueue
32451             position.item = item;
32452             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32453             queue.push( position );
32454         }, this);
32455       
32456         this._processLayoutQueue( queue );
32457     },
32458     /** Sets position of item in DOM
32459     * @param {Element} item
32460     * @param {Number} x - horizontal position
32461     * @param {Number} y - vertical position
32462     * @param {Boolean} isInstant - disables transitions
32463     */
32464     _processLayoutQueue : function( queue )
32465     {
32466         for ( var i=0, len = queue.length; i < len; i++ ) {
32467             var obj = queue[i];
32468             obj.item.position('absolute');
32469             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32470         }
32471     },
32472       
32473     
32474     /**
32475     * Any logic you want to do after each layout,
32476     * i.e. size the container
32477     */
32478     _postLayout : function()
32479     {
32480         this.resizeContainer();
32481     },
32482     
32483     resizeContainer : function()
32484     {
32485         if ( !this.isResizingContainer ) {
32486             return;
32487         }
32488         var size = this._getContainerSize();
32489         if ( size ) {
32490             this.el.setSize(size.width,size.height);
32491             this.boxesEl.setSize(size.width,size.height);
32492         }
32493     },
32494     
32495     
32496     
32497     _resetLayout : function()
32498     {
32499         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32500         this.colWidth = this.el.getWidth();
32501         //this.gutter = this.el.getWidth(); 
32502         
32503         this.measureColumns();
32504
32505         // reset column Y
32506         var i = this.cols;
32507         this.colYs = [];
32508         while (i--) {
32509             this.colYs.push( 0 );
32510         }
32511     
32512         this.maxY = 0;
32513     },
32514
32515     measureColumns : function()
32516     {
32517         this.getContainerWidth();
32518       // if columnWidth is 0, default to outerWidth of first item
32519         if ( !this.columnWidth ) {
32520             var firstItem = this.bricks.first();
32521             Roo.log(firstItem);
32522             this.columnWidth  = this.containerWidth;
32523             if (firstItem && firstItem.attr('originalwidth') ) {
32524                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32525             }
32526             // columnWidth fall back to item of first element
32527             Roo.log("set column width?");
32528                         this.initialColumnWidth = this.columnWidth  ;
32529
32530             // if first elem has no width, default to size of container
32531             
32532         }
32533         
32534         
32535         if (this.initialColumnWidth) {
32536             this.columnWidth = this.initialColumnWidth;
32537         }
32538         
32539         
32540             
32541         // column width is fixed at the top - however if container width get's smaller we should
32542         // reduce it...
32543         
32544         // this bit calcs how man columns..
32545             
32546         var columnWidth = this.columnWidth += this.gutter;
32547       
32548         // calculate columns
32549         var containerWidth = this.containerWidth + this.gutter;
32550         
32551         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32552         // fix rounding errors, typically with gutters
32553         var excess = columnWidth - containerWidth % columnWidth;
32554         
32555         
32556         // if overshoot is less than a pixel, round up, otherwise floor it
32557         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32558         cols = Math[ mathMethod ]( cols );
32559         this.cols = Math.max( cols, 1 );
32560         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32561         
32562          // padding positioning..
32563         var totalColWidth = this.cols * this.columnWidth;
32564         var padavail = this.containerWidth - totalColWidth;
32565         // so for 2 columns - we need 3 'pads'
32566         
32567         var padNeeded = (1+this.cols) * this.padWidth;
32568         
32569         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32570         
32571         this.columnWidth += padExtra
32572         //this.padWidth = Math.floor(padavail /  ( this.cols));
32573         
32574         // adjust colum width so that padding is fixed??
32575         
32576         // we have 3 columns ... total = width * 3
32577         // we have X left over... that should be used by 
32578         
32579         //if (this.expandC) {
32580             
32581         //}
32582         
32583         
32584         
32585     },
32586     
32587     getContainerWidth : function()
32588     {
32589        /* // container is parent if fit width
32590         var container = this.isFitWidth ? this.element.parentNode : this.element;
32591         // check that this.size and size are there
32592         // IE8 triggers resize on body size change, so they might not be
32593         
32594         var size = getSize( container );  //FIXME
32595         this.containerWidth = size && size.innerWidth; //FIXME
32596         */
32597          
32598         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32599         
32600     },
32601     
32602     _getItemLayoutPosition : function( item )  // what is item?
32603     {
32604         // we resize the item to our columnWidth..
32605       
32606         item.setWidth(this.columnWidth);
32607         item.autoBoxAdjust  = false;
32608         
32609         var sz = item.getSize();
32610  
32611         // how many columns does this brick span
32612         var remainder = this.containerWidth % this.columnWidth;
32613         
32614         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32615         // round if off by 1 pixel, otherwise use ceil
32616         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32617         colSpan = Math.min( colSpan, this.cols );
32618         
32619         // normally this should be '1' as we dont' currently allow multi width columns..
32620         
32621         var colGroup = this._getColGroup( colSpan );
32622         // get the minimum Y value from the columns
32623         var minimumY = Math.min.apply( Math, colGroup );
32624         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32625         
32626         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32627          
32628         // position the brick
32629         var position = {
32630             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32631             y: this.currentSize.y + minimumY + this.padHeight
32632         };
32633         
32634         Roo.log(position);
32635         // apply setHeight to necessary columns
32636         var setHeight = minimumY + sz.height + this.padHeight;
32637         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32638         
32639         var setSpan = this.cols + 1 - colGroup.length;
32640         for ( var i = 0; i < setSpan; i++ ) {
32641           this.colYs[ shortColIndex + i ] = setHeight ;
32642         }
32643       
32644         return position;
32645     },
32646     
32647     /**
32648      * @param {Number} colSpan - number of columns the element spans
32649      * @returns {Array} colGroup
32650      */
32651     _getColGroup : function( colSpan )
32652     {
32653         if ( colSpan < 2 ) {
32654           // if brick spans only one column, use all the column Ys
32655           return this.colYs;
32656         }
32657       
32658         var colGroup = [];
32659         // how many different places could this brick fit horizontally
32660         var groupCount = this.cols + 1 - colSpan;
32661         // for each group potential horizontal position
32662         for ( var i = 0; i < groupCount; i++ ) {
32663           // make an array of colY values for that one group
32664           var groupColYs = this.colYs.slice( i, i + colSpan );
32665           // and get the max value of the array
32666           colGroup[i] = Math.max.apply( Math, groupColYs );
32667         }
32668         return colGroup;
32669     },
32670     /*
32671     _manageStamp : function( stamp )
32672     {
32673         var stampSize =  stamp.getSize();
32674         var offset = stamp.getBox();
32675         // get the columns that this stamp affects
32676         var firstX = this.isOriginLeft ? offset.x : offset.right;
32677         var lastX = firstX + stampSize.width;
32678         var firstCol = Math.floor( firstX / this.columnWidth );
32679         firstCol = Math.max( 0, firstCol );
32680         
32681         var lastCol = Math.floor( lastX / this.columnWidth );
32682         // lastCol should not go over if multiple of columnWidth #425
32683         lastCol -= lastX % this.columnWidth ? 0 : 1;
32684         lastCol = Math.min( this.cols - 1, lastCol );
32685         
32686         // set colYs to bottom of the stamp
32687         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32688             stampSize.height;
32689             
32690         for ( var i = firstCol; i <= lastCol; i++ ) {
32691           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32692         }
32693     },
32694     */
32695     
32696     _getContainerSize : function()
32697     {
32698         this.maxY = Math.max.apply( Math, this.colYs );
32699         var size = {
32700             height: this.maxY
32701         };
32702       
32703         if ( this.isFitWidth ) {
32704             size.width = this._getContainerFitWidth();
32705         }
32706       
32707         return size;
32708     },
32709     
32710     _getContainerFitWidth : function()
32711     {
32712         var unusedCols = 0;
32713         // count unused columns
32714         var i = this.cols;
32715         while ( --i ) {
32716           if ( this.colYs[i] !== 0 ) {
32717             break;
32718           }
32719           unusedCols++;
32720         }
32721         // fit container to columns that have been used
32722         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32723     },
32724     
32725     needsResizeLayout : function()
32726     {
32727         var previousWidth = this.containerWidth;
32728         this.getContainerWidth();
32729         return previousWidth !== this.containerWidth;
32730     }
32731  
32732 });
32733
32734  
32735
32736  /*
32737  * - LGPL
32738  *
32739  * element
32740  * 
32741  */
32742
32743 /**
32744  * @class Roo.bootstrap.MasonryBrick
32745  * @extends Roo.bootstrap.Component
32746  * Bootstrap MasonryBrick class
32747  * 
32748  * @constructor
32749  * Create a new MasonryBrick
32750  * @param {Object} config The config object
32751  */
32752
32753 Roo.bootstrap.MasonryBrick = function(config){
32754     
32755     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32756     
32757     Roo.bootstrap.MasonryBrick.register(this);
32758     
32759     this.addEvents({
32760         // raw events
32761         /**
32762          * @event click
32763          * When a MasonryBrick is clcik
32764          * @param {Roo.bootstrap.MasonryBrick} this
32765          * @param {Roo.EventObject} e
32766          */
32767         "click" : true
32768     });
32769 };
32770
32771 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32772     
32773     /**
32774      * @cfg {String} title
32775      */   
32776     title : '',
32777     /**
32778      * @cfg {String} html
32779      */   
32780     html : '',
32781     /**
32782      * @cfg {String} bgimage
32783      */   
32784     bgimage : '',
32785     /**
32786      * @cfg {String} videourl
32787      */   
32788     videourl : '',
32789     /**
32790      * @cfg {String} cls
32791      */   
32792     cls : '',
32793     /**
32794      * @cfg {String} href
32795      */   
32796     href : '',
32797     /**
32798      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32799      */   
32800     size : 'xs',
32801     
32802     /**
32803      * @cfg {String} placetitle (center|bottom)
32804      */   
32805     placetitle : '',
32806     
32807     /**
32808      * @cfg {Boolean} isFitContainer defalut true
32809      */   
32810     isFitContainer : true, 
32811     
32812     /**
32813      * @cfg {Boolean} preventDefault defalut false
32814      */   
32815     preventDefault : false, 
32816     
32817     /**
32818      * @cfg {Boolean} inverse defalut false
32819      */   
32820     maskInverse : false, 
32821     
32822     getAutoCreate : function()
32823     {
32824         if(!this.isFitContainer){
32825             return this.getSplitAutoCreate();
32826         }
32827         
32828         var cls = 'masonry-brick masonry-brick-full';
32829         
32830         if(this.href.length){
32831             cls += ' masonry-brick-link';
32832         }
32833         
32834         if(this.bgimage.length){
32835             cls += ' masonry-brick-image';
32836         }
32837         
32838         if(this.maskInverse){
32839             cls += ' mask-inverse';
32840         }
32841         
32842         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32843             cls += ' enable-mask';
32844         }
32845         
32846         if(this.size){
32847             cls += ' masonry-' + this.size + '-brick';
32848         }
32849         
32850         if(this.placetitle.length){
32851             
32852             switch (this.placetitle) {
32853                 case 'center' :
32854                     cls += ' masonry-center-title';
32855                     break;
32856                 case 'bottom' :
32857                     cls += ' masonry-bottom-title';
32858                     break;
32859                 default:
32860                     break;
32861             }
32862             
32863         } else {
32864             if(!this.html.length && !this.bgimage.length){
32865                 cls += ' masonry-center-title';
32866             }
32867
32868             if(!this.html.length && this.bgimage.length){
32869                 cls += ' masonry-bottom-title';
32870             }
32871         }
32872         
32873         if(this.cls){
32874             cls += ' ' + this.cls;
32875         }
32876         
32877         var cfg = {
32878             tag: (this.href.length) ? 'a' : 'div',
32879             cls: cls,
32880             cn: [
32881                 {
32882                     tag: 'div',
32883                     cls: 'masonry-brick-mask'
32884                 },
32885                 {
32886                     tag: 'div',
32887                     cls: 'masonry-brick-paragraph',
32888                     cn: []
32889                 }
32890             ]
32891         };
32892         
32893         if(this.href.length){
32894             cfg.href = this.href;
32895         }
32896         
32897         var cn = cfg.cn[1].cn;
32898         
32899         if(this.title.length){
32900             cn.push({
32901                 tag: 'h4',
32902                 cls: 'masonry-brick-title',
32903                 html: this.title
32904             });
32905         }
32906         
32907         if(this.html.length){
32908             cn.push({
32909                 tag: 'p',
32910                 cls: 'masonry-brick-text',
32911                 html: this.html
32912             });
32913         }
32914         
32915         if (!this.title.length && !this.html.length) {
32916             cfg.cn[1].cls += ' hide';
32917         }
32918         
32919         if(this.bgimage.length){
32920             cfg.cn.push({
32921                 tag: 'img',
32922                 cls: 'masonry-brick-image-view',
32923                 src: this.bgimage
32924             });
32925         }
32926         
32927         if(this.videourl.length){
32928             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32929             // youtube support only?
32930             cfg.cn.push({
32931                 tag: 'iframe',
32932                 cls: 'masonry-brick-image-view',
32933                 src: vurl,
32934                 frameborder : 0,
32935                 allowfullscreen : true
32936             });
32937         }
32938         
32939         return cfg;
32940         
32941     },
32942     
32943     getSplitAutoCreate : function()
32944     {
32945         var cls = 'masonry-brick masonry-brick-split';
32946         
32947         if(this.href.length){
32948             cls += ' masonry-brick-link';
32949         }
32950         
32951         if(this.bgimage.length){
32952             cls += ' masonry-brick-image';
32953         }
32954         
32955         if(this.size){
32956             cls += ' masonry-' + this.size + '-brick';
32957         }
32958         
32959         switch (this.placetitle) {
32960             case 'center' :
32961                 cls += ' masonry-center-title';
32962                 break;
32963             case 'bottom' :
32964                 cls += ' masonry-bottom-title';
32965                 break;
32966             default:
32967                 if(!this.bgimage.length){
32968                     cls += ' masonry-center-title';
32969                 }
32970
32971                 if(this.bgimage.length){
32972                     cls += ' masonry-bottom-title';
32973                 }
32974                 break;
32975         }
32976         
32977         if(this.cls){
32978             cls += ' ' + this.cls;
32979         }
32980         
32981         var cfg = {
32982             tag: (this.href.length) ? 'a' : 'div',
32983             cls: cls,
32984             cn: [
32985                 {
32986                     tag: 'div',
32987                     cls: 'masonry-brick-split-head',
32988                     cn: [
32989                         {
32990                             tag: 'div',
32991                             cls: 'masonry-brick-paragraph',
32992                             cn: []
32993                         }
32994                     ]
32995                 },
32996                 {
32997                     tag: 'div',
32998                     cls: 'masonry-brick-split-body',
32999                     cn: []
33000                 }
33001             ]
33002         };
33003         
33004         if(this.href.length){
33005             cfg.href = this.href;
33006         }
33007         
33008         if(this.title.length){
33009             cfg.cn[0].cn[0].cn.push({
33010                 tag: 'h4',
33011                 cls: 'masonry-brick-title',
33012                 html: this.title
33013             });
33014         }
33015         
33016         if(this.html.length){
33017             cfg.cn[1].cn.push({
33018                 tag: 'p',
33019                 cls: 'masonry-brick-text',
33020                 html: this.html
33021             });
33022         }
33023
33024         if(this.bgimage.length){
33025             cfg.cn[0].cn.push({
33026                 tag: 'img',
33027                 cls: 'masonry-brick-image-view',
33028                 src: this.bgimage
33029             });
33030         }
33031         
33032         if(this.videourl.length){
33033             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33034             // youtube support only?
33035             cfg.cn[0].cn.cn.push({
33036                 tag: 'iframe',
33037                 cls: 'masonry-brick-image-view',
33038                 src: vurl,
33039                 frameborder : 0,
33040                 allowfullscreen : true
33041             });
33042         }
33043         
33044         return cfg;
33045     },
33046     
33047     initEvents: function() 
33048     {
33049         switch (this.size) {
33050             case 'xs' :
33051                 this.x = 1;
33052                 this.y = 1;
33053                 break;
33054             case 'sm' :
33055                 this.x = 2;
33056                 this.y = 2;
33057                 break;
33058             case 'md' :
33059             case 'md-left' :
33060             case 'md-right' :
33061                 this.x = 3;
33062                 this.y = 3;
33063                 break;
33064             case 'tall' :
33065                 this.x = 2;
33066                 this.y = 3;
33067                 break;
33068             case 'wide' :
33069                 this.x = 3;
33070                 this.y = 2;
33071                 break;
33072             case 'wide-thin' :
33073                 this.x = 3;
33074                 this.y = 1;
33075                 break;
33076                         
33077             default :
33078                 break;
33079         }
33080         
33081         if(Roo.isTouch){
33082             this.el.on('touchstart', this.onTouchStart, this);
33083             this.el.on('touchmove', this.onTouchMove, this);
33084             this.el.on('touchend', this.onTouchEnd, this);
33085             this.el.on('contextmenu', this.onContextMenu, this);
33086         } else {
33087             this.el.on('mouseenter'  ,this.enter, this);
33088             this.el.on('mouseleave', this.leave, this);
33089             this.el.on('click', this.onClick, this);
33090         }
33091         
33092         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33093             this.parent().bricks.push(this);   
33094         }
33095         
33096     },
33097     
33098     onClick: function(e, el)
33099     {
33100         var time = this.endTimer - this.startTimer;
33101         // Roo.log(e.preventDefault());
33102         if(Roo.isTouch){
33103             if(time > 1000){
33104                 e.preventDefault();
33105                 return;
33106             }
33107         }
33108         
33109         if(!this.preventDefault){
33110             return;
33111         }
33112         
33113         e.preventDefault();
33114         
33115         if (this.activeClass != '') {
33116             this.selectBrick();
33117         }
33118         
33119         this.fireEvent('click', this, e);
33120     },
33121     
33122     enter: function(e, el)
33123     {
33124         e.preventDefault();
33125         
33126         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33127             return;
33128         }
33129         
33130         if(this.bgimage.length && this.html.length){
33131             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33132         }
33133     },
33134     
33135     leave: function(e, el)
33136     {
33137         e.preventDefault();
33138         
33139         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33140             return;
33141         }
33142         
33143         if(this.bgimage.length && this.html.length){
33144             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33145         }
33146     },
33147     
33148     onTouchStart: function(e, el)
33149     {
33150 //        e.preventDefault();
33151         
33152         this.touchmoved = false;
33153         
33154         if(!this.isFitContainer){
33155             return;
33156         }
33157         
33158         if(!this.bgimage.length || !this.html.length){
33159             return;
33160         }
33161         
33162         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33163         
33164         this.timer = new Date().getTime();
33165         
33166     },
33167     
33168     onTouchMove: function(e, el)
33169     {
33170         this.touchmoved = true;
33171     },
33172     
33173     onContextMenu : function(e,el)
33174     {
33175         e.preventDefault();
33176         e.stopPropagation();
33177         return false;
33178     },
33179     
33180     onTouchEnd: function(e, el)
33181     {
33182 //        e.preventDefault();
33183         
33184         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33185         
33186             this.leave(e,el);
33187             
33188             return;
33189         }
33190         
33191         if(!this.bgimage.length || !this.html.length){
33192             
33193             if(this.href.length){
33194                 window.location.href = this.href;
33195             }
33196             
33197             return;
33198         }
33199         
33200         if(!this.isFitContainer){
33201             return;
33202         }
33203         
33204         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33205         
33206         window.location.href = this.href;
33207     },
33208     
33209     //selection on single brick only
33210     selectBrick : function() {
33211         
33212         if (!this.parentId) {
33213             return;
33214         }
33215         
33216         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33217         var index = m.selectedBrick.indexOf(this.id);
33218         
33219         if ( index > -1) {
33220             m.selectedBrick.splice(index,1);
33221             this.el.removeClass(this.activeClass);
33222             return;
33223         }
33224         
33225         for(var i = 0; i < m.selectedBrick.length; i++) {
33226             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33227             b.el.removeClass(b.activeClass);
33228         }
33229         
33230         m.selectedBrick = [];
33231         
33232         m.selectedBrick.push(this.id);
33233         this.el.addClass(this.activeClass);
33234         return;
33235     },
33236     
33237     isSelected : function(){
33238         return this.el.hasClass(this.activeClass);
33239         
33240     }
33241 });
33242
33243 Roo.apply(Roo.bootstrap.MasonryBrick, {
33244     
33245     //groups: {},
33246     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33247      /**
33248     * register a Masonry Brick
33249     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33250     */
33251     
33252     register : function(brick)
33253     {
33254         //this.groups[brick.id] = brick;
33255         this.groups.add(brick.id, brick);
33256     },
33257     /**
33258     * fetch a  masonry brick based on the masonry brick ID
33259     * @param {string} the masonry brick to add
33260     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33261     */
33262     
33263     get: function(brick_id) 
33264     {
33265         // if (typeof(this.groups[brick_id]) == 'undefined') {
33266         //     return false;
33267         // }
33268         // return this.groups[brick_id] ;
33269         
33270         if(this.groups.key(brick_id)) {
33271             return this.groups.key(brick_id);
33272         }
33273         
33274         return false;
33275     }
33276     
33277     
33278     
33279 });
33280
33281  /*
33282  * - LGPL
33283  *
33284  * element
33285  * 
33286  */
33287
33288 /**
33289  * @class Roo.bootstrap.Brick
33290  * @extends Roo.bootstrap.Component
33291  * Bootstrap Brick class
33292  * 
33293  * @constructor
33294  * Create a new Brick
33295  * @param {Object} config The config object
33296  */
33297
33298 Roo.bootstrap.Brick = function(config){
33299     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33300     
33301     this.addEvents({
33302         // raw events
33303         /**
33304          * @event click
33305          * When a Brick is click
33306          * @param {Roo.bootstrap.Brick} this
33307          * @param {Roo.EventObject} e
33308          */
33309         "click" : true
33310     });
33311 };
33312
33313 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33314     
33315     /**
33316      * @cfg {String} title
33317      */   
33318     title : '',
33319     /**
33320      * @cfg {String} html
33321      */   
33322     html : '',
33323     /**
33324      * @cfg {String} bgimage
33325      */   
33326     bgimage : '',
33327     /**
33328      * @cfg {String} cls
33329      */   
33330     cls : '',
33331     /**
33332      * @cfg {String} href
33333      */   
33334     href : '',
33335     /**
33336      * @cfg {String} video
33337      */   
33338     video : '',
33339     /**
33340      * @cfg {Boolean} square
33341      */   
33342     square : true,
33343     
33344     getAutoCreate : function()
33345     {
33346         var cls = 'roo-brick';
33347         
33348         if(this.href.length){
33349             cls += ' roo-brick-link';
33350         }
33351         
33352         if(this.bgimage.length){
33353             cls += ' roo-brick-image';
33354         }
33355         
33356         if(!this.html.length && !this.bgimage.length){
33357             cls += ' roo-brick-center-title';
33358         }
33359         
33360         if(!this.html.length && this.bgimage.length){
33361             cls += ' roo-brick-bottom-title';
33362         }
33363         
33364         if(this.cls){
33365             cls += ' ' + this.cls;
33366         }
33367         
33368         var cfg = {
33369             tag: (this.href.length) ? 'a' : 'div',
33370             cls: cls,
33371             cn: [
33372                 {
33373                     tag: 'div',
33374                     cls: 'roo-brick-paragraph',
33375                     cn: []
33376                 }
33377             ]
33378         };
33379         
33380         if(this.href.length){
33381             cfg.href = this.href;
33382         }
33383         
33384         var cn = cfg.cn[0].cn;
33385         
33386         if(this.title.length){
33387             cn.push({
33388                 tag: 'h4',
33389                 cls: 'roo-brick-title',
33390                 html: this.title
33391             });
33392         }
33393         
33394         if(this.html.length){
33395             cn.push({
33396                 tag: 'p',
33397                 cls: 'roo-brick-text',
33398                 html: this.html
33399             });
33400         } else {
33401             cn.cls += ' hide';
33402         }
33403         
33404         if(this.bgimage.length){
33405             cfg.cn.push({
33406                 tag: 'img',
33407                 cls: 'roo-brick-image-view',
33408                 src: this.bgimage
33409             });
33410         }
33411         
33412         return cfg;
33413     },
33414     
33415     initEvents: function() 
33416     {
33417         if(this.title.length || this.html.length){
33418             this.el.on('mouseenter'  ,this.enter, this);
33419             this.el.on('mouseleave', this.leave, this);
33420         }
33421         
33422         Roo.EventManager.onWindowResize(this.resize, this); 
33423         
33424         if(this.bgimage.length){
33425             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33426             this.imageEl.on('load', this.onImageLoad, this);
33427             return;
33428         }
33429         
33430         this.resize();
33431     },
33432     
33433     onImageLoad : function()
33434     {
33435         this.resize();
33436     },
33437     
33438     resize : function()
33439     {
33440         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33441         
33442         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33443         
33444         if(this.bgimage.length){
33445             var image = this.el.select('.roo-brick-image-view', true).first();
33446             
33447             image.setWidth(paragraph.getWidth());
33448             
33449             if(this.square){
33450                 image.setHeight(paragraph.getWidth());
33451             }
33452             
33453             this.el.setHeight(image.getHeight());
33454             paragraph.setHeight(image.getHeight());
33455             
33456         }
33457         
33458     },
33459     
33460     enter: function(e, el)
33461     {
33462         e.preventDefault();
33463         
33464         if(this.bgimage.length){
33465             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33466             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33467         }
33468     },
33469     
33470     leave: function(e, el)
33471     {
33472         e.preventDefault();
33473         
33474         if(this.bgimage.length){
33475             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33476             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33477         }
33478     }
33479     
33480 });
33481
33482  
33483
33484  /*
33485  * - LGPL
33486  *
33487  * Number field 
33488  */
33489
33490 /**
33491  * @class Roo.bootstrap.NumberField
33492  * @extends Roo.bootstrap.Input
33493  * Bootstrap NumberField class
33494  * 
33495  * 
33496  * 
33497  * 
33498  * @constructor
33499  * Create a new NumberField
33500  * @param {Object} config The config object
33501  */
33502
33503 Roo.bootstrap.NumberField = function(config){
33504     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33505 };
33506
33507 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33508     
33509     /**
33510      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33511      */
33512     allowDecimals : true,
33513     /**
33514      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33515      */
33516     decimalSeparator : ".",
33517     /**
33518      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33519      */
33520     decimalPrecision : 2,
33521     /**
33522      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33523      */
33524     allowNegative : true,
33525     
33526     /**
33527      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33528      */
33529     allowZero: true,
33530     /**
33531      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33532      */
33533     minValue : Number.NEGATIVE_INFINITY,
33534     /**
33535      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33536      */
33537     maxValue : Number.MAX_VALUE,
33538     /**
33539      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33540      */
33541     minText : "The minimum value for this field is {0}",
33542     /**
33543      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33544      */
33545     maxText : "The maximum value for this field is {0}",
33546     /**
33547      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33548      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33549      */
33550     nanText : "{0} is not a valid number",
33551     /**
33552      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33553      */
33554     thousandsDelimiter : false,
33555     /**
33556      * @cfg {String} valueAlign alignment of value
33557      */
33558     valueAlign : "left",
33559
33560     getAutoCreate : function()
33561     {
33562         var hiddenInput = {
33563             tag: 'input',
33564             type: 'hidden',
33565             id: Roo.id(),
33566             cls: 'hidden-number-input'
33567         };
33568         
33569         if (this.name) {
33570             hiddenInput.name = this.name;
33571         }
33572         
33573         this.name = '';
33574         
33575         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33576         
33577         this.name = hiddenInput.name;
33578         
33579         if(cfg.cn.length > 0) {
33580             cfg.cn.push(hiddenInput);
33581         }
33582         
33583         return cfg;
33584     },
33585
33586     // private
33587     initEvents : function()
33588     {   
33589         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33590         
33591         var allowed = "0123456789";
33592         
33593         if(this.allowDecimals){
33594             allowed += this.decimalSeparator;
33595         }
33596         
33597         if(this.allowNegative){
33598             allowed += "-";
33599         }
33600         
33601         if(this.thousandsDelimiter) {
33602             allowed += ",";
33603         }
33604         
33605         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33606         
33607         var keyPress = function(e){
33608             
33609             var k = e.getKey();
33610             
33611             var c = e.getCharCode();
33612             
33613             if(
33614                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33615                     allowed.indexOf(String.fromCharCode(c)) === -1
33616             ){
33617                 e.stopEvent();
33618                 return;
33619             }
33620             
33621             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33622                 return;
33623             }
33624             
33625             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33626                 e.stopEvent();
33627             }
33628         };
33629         
33630         this.el.on("keypress", keyPress, this);
33631     },
33632     
33633     validateValue : function(value)
33634     {
33635         
33636         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33637             return false;
33638         }
33639         
33640         var num = this.parseValue(value);
33641         
33642         if(isNaN(num)){
33643             this.markInvalid(String.format(this.nanText, value));
33644             return false;
33645         }
33646         
33647         if(num < this.minValue){
33648             this.markInvalid(String.format(this.minText, this.minValue));
33649             return false;
33650         }
33651         
33652         if(num > this.maxValue){
33653             this.markInvalid(String.format(this.maxText, this.maxValue));
33654             return false;
33655         }
33656         
33657         return true;
33658     },
33659
33660     getValue : function()
33661     {
33662         var v = this.hiddenEl().getValue();
33663         
33664         return this.fixPrecision(this.parseValue(v));
33665     },
33666
33667     parseValue : function(value)
33668     {
33669         if(this.thousandsDelimiter) {
33670             value += "";
33671             r = new RegExp(",", "g");
33672             value = value.replace(r, "");
33673         }
33674         
33675         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33676         return isNaN(value) ? '' : value;
33677     },
33678
33679     fixPrecision : function(value)
33680     {
33681         if(this.thousandsDelimiter) {
33682             value += "";
33683             r = new RegExp(",", "g");
33684             value = value.replace(r, "");
33685         }
33686         
33687         var nan = isNaN(value);
33688         
33689         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33690             return nan ? '' : value;
33691         }
33692         return parseFloat(value).toFixed(this.decimalPrecision);
33693     },
33694
33695     setValue : function(v)
33696     {
33697         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33698         
33699         this.value = v;
33700         
33701         if(this.rendered){
33702             
33703             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33704             
33705             this.inputEl().dom.value = (v == '') ? '' :
33706                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33707             
33708             if(!this.allowZero && v === '0') {
33709                 this.hiddenEl().dom.value = '';
33710                 this.inputEl().dom.value = '';
33711             }
33712             
33713             this.validate();
33714         }
33715     },
33716
33717     decimalPrecisionFcn : function(v)
33718     {
33719         return Math.floor(v);
33720     },
33721
33722     beforeBlur : function()
33723     {
33724         var v = this.parseValue(this.getRawValue());
33725         
33726         if(v || v === 0 || v === ''){
33727             this.setValue(v);
33728         }
33729     },
33730     
33731     hiddenEl : function()
33732     {
33733         return this.el.select('input.hidden-number-input',true).first();
33734     }
33735     
33736 });
33737
33738  
33739
33740 /*
33741 * Licence: LGPL
33742 */
33743
33744 /**
33745  * @class Roo.bootstrap.DocumentSlider
33746  * @extends Roo.bootstrap.Component
33747  * Bootstrap DocumentSlider class
33748  * 
33749  * @constructor
33750  * Create a new DocumentViewer
33751  * @param {Object} config The config object
33752  */
33753
33754 Roo.bootstrap.DocumentSlider = function(config){
33755     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33756     
33757     this.files = [];
33758     
33759     this.addEvents({
33760         /**
33761          * @event initial
33762          * Fire after initEvent
33763          * @param {Roo.bootstrap.DocumentSlider} this
33764          */
33765         "initial" : true,
33766         /**
33767          * @event update
33768          * Fire after update
33769          * @param {Roo.bootstrap.DocumentSlider} this
33770          */
33771         "update" : true,
33772         /**
33773          * @event click
33774          * Fire after click
33775          * @param {Roo.bootstrap.DocumentSlider} this
33776          */
33777         "click" : true
33778     });
33779 };
33780
33781 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33782     
33783     files : false,
33784     
33785     indicator : 0,
33786     
33787     getAutoCreate : function()
33788     {
33789         var cfg = {
33790             tag : 'div',
33791             cls : 'roo-document-slider',
33792             cn : [
33793                 {
33794                     tag : 'div',
33795                     cls : 'roo-document-slider-header',
33796                     cn : [
33797                         {
33798                             tag : 'div',
33799                             cls : 'roo-document-slider-header-title'
33800                         }
33801                     ]
33802                 },
33803                 {
33804                     tag : 'div',
33805                     cls : 'roo-document-slider-body',
33806                     cn : [
33807                         {
33808                             tag : 'div',
33809                             cls : 'roo-document-slider-prev',
33810                             cn : [
33811                                 {
33812                                     tag : 'i',
33813                                     cls : 'fa fa-chevron-left'
33814                                 }
33815                             ]
33816                         },
33817                         {
33818                             tag : 'div',
33819                             cls : 'roo-document-slider-thumb',
33820                             cn : [
33821                                 {
33822                                     tag : 'img',
33823                                     cls : 'roo-document-slider-image'
33824                                 }
33825                             ]
33826                         },
33827                         {
33828                             tag : 'div',
33829                             cls : 'roo-document-slider-next',
33830                             cn : [
33831                                 {
33832                                     tag : 'i',
33833                                     cls : 'fa fa-chevron-right'
33834                                 }
33835                             ]
33836                         }
33837                     ]
33838                 }
33839             ]
33840         };
33841         
33842         return cfg;
33843     },
33844     
33845     initEvents : function()
33846     {
33847         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33848         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33849         
33850         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33851         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33852         
33853         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33854         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33855         
33856         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33857         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33858         
33859         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33860         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33861         
33862         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33863         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33864         
33865         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33866         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33867         
33868         this.thumbEl.on('click', this.onClick, this);
33869         
33870         this.prevIndicator.on('click', this.prev, this);
33871         
33872         this.nextIndicator.on('click', this.next, this);
33873         
33874     },
33875     
33876     initial : function()
33877     {
33878         if(this.files.length){
33879             this.indicator = 1;
33880             this.update()
33881         }
33882         
33883         this.fireEvent('initial', this);
33884     },
33885     
33886     update : function()
33887     {
33888         this.imageEl.attr('src', this.files[this.indicator - 1]);
33889         
33890         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33891         
33892         this.prevIndicator.show();
33893         
33894         if(this.indicator == 1){
33895             this.prevIndicator.hide();
33896         }
33897         
33898         this.nextIndicator.show();
33899         
33900         if(this.indicator == this.files.length){
33901             this.nextIndicator.hide();
33902         }
33903         
33904         this.thumbEl.scrollTo('top');
33905         
33906         this.fireEvent('update', this);
33907     },
33908     
33909     onClick : function(e)
33910     {
33911         e.preventDefault();
33912         
33913         this.fireEvent('click', this);
33914     },
33915     
33916     prev : function(e)
33917     {
33918         e.preventDefault();
33919         
33920         this.indicator = Math.max(1, this.indicator - 1);
33921         
33922         this.update();
33923     },
33924     
33925     next : function(e)
33926     {
33927         e.preventDefault();
33928         
33929         this.indicator = Math.min(this.files.length, this.indicator + 1);
33930         
33931         this.update();
33932     }
33933 });
33934 /*
33935  * - LGPL
33936  *
33937  * RadioSet
33938  *
33939  *
33940  */
33941
33942 /**
33943  * @class Roo.bootstrap.RadioSet
33944  * @extends Roo.bootstrap.Input
33945  * Bootstrap RadioSet class
33946  * @cfg {String} indicatorpos (left|right) default left
33947  * @cfg {Boolean} inline (true|false) inline the element (default true)
33948  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33949  * @constructor
33950  * Create a new RadioSet
33951  * @param {Object} config The config object
33952  */
33953
33954 Roo.bootstrap.RadioSet = function(config){
33955     
33956     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33957     
33958     this.radioes = [];
33959     
33960     Roo.bootstrap.RadioSet.register(this);
33961     
33962     this.addEvents({
33963         /**
33964         * @event check
33965         * Fires when the element is checked or unchecked.
33966         * @param {Roo.bootstrap.RadioSet} this This radio
33967         * @param {Roo.bootstrap.Radio} item The checked item
33968         */
33969        check : true,
33970        /**
33971         * @event click
33972         * Fires when the element is click.
33973         * @param {Roo.bootstrap.RadioSet} this This radio set
33974         * @param {Roo.bootstrap.Radio} item The checked item
33975         * @param {Roo.EventObject} e The event object
33976         */
33977        click : true
33978     });
33979     
33980 };
33981
33982 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33983
33984     radioes : false,
33985     
33986     inline : true,
33987     
33988     weight : '',
33989     
33990     indicatorpos : 'left',
33991     
33992     getAutoCreate : function()
33993     {
33994         var label = {
33995             tag : 'label',
33996             cls : 'roo-radio-set-label',
33997             cn : [
33998                 {
33999                     tag : 'span',
34000                     html : this.fieldLabel
34001                 }
34002             ]
34003         };
34004         
34005         if(this.indicatorpos == 'left'){
34006             label.cn.unshift({
34007                 tag : 'i',
34008                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34009                 tooltip : 'This field is required'
34010             });
34011         } else {
34012             label.cn.push({
34013                 tag : 'i',
34014                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34015                 tooltip : 'This field is required'
34016             });
34017         }
34018         
34019         var items = {
34020             tag : 'div',
34021             cls : 'roo-radio-set-items'
34022         };
34023         
34024         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34025         
34026         if (align === 'left' && this.fieldLabel.length) {
34027             
34028             items = {
34029                 cls : "roo-radio-set-right", 
34030                 cn: [
34031                     items
34032                 ]
34033             };
34034             
34035             if(this.labelWidth > 12){
34036                 label.style = "width: " + this.labelWidth + 'px';
34037             }
34038             
34039             if(this.labelWidth < 13 && this.labelmd == 0){
34040                 this.labelmd = this.labelWidth;
34041             }
34042             
34043             if(this.labellg > 0){
34044                 label.cls += ' col-lg-' + this.labellg;
34045                 items.cls += ' col-lg-' + (12 - this.labellg);
34046             }
34047             
34048             if(this.labelmd > 0){
34049                 label.cls += ' col-md-' + this.labelmd;
34050                 items.cls += ' col-md-' + (12 - this.labelmd);
34051             }
34052             
34053             if(this.labelsm > 0){
34054                 label.cls += ' col-sm-' + this.labelsm;
34055                 items.cls += ' col-sm-' + (12 - this.labelsm);
34056             }
34057             
34058             if(this.labelxs > 0){
34059                 label.cls += ' col-xs-' + this.labelxs;
34060                 items.cls += ' col-xs-' + (12 - this.labelxs);
34061             }
34062         }
34063         
34064         var cfg = {
34065             tag : 'div',
34066             cls : 'roo-radio-set',
34067             cn : [
34068                 {
34069                     tag : 'input',
34070                     cls : 'roo-radio-set-input',
34071                     type : 'hidden',
34072                     name : this.name,
34073                     value : this.value ? this.value :  ''
34074                 },
34075                 label,
34076                 items
34077             ]
34078         };
34079         
34080         if(this.weight.length){
34081             cfg.cls += ' roo-radio-' + this.weight;
34082         }
34083         
34084         if(this.inline) {
34085             cfg.cls += ' roo-radio-set-inline';
34086         }
34087         
34088         var settings=this;
34089         ['xs','sm','md','lg'].map(function(size){
34090             if (settings[size]) {
34091                 cfg.cls += ' col-' + size + '-' + settings[size];
34092             }
34093         });
34094         
34095         return cfg;
34096         
34097     },
34098
34099     initEvents : function()
34100     {
34101         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34102         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34103         
34104         if(!this.fieldLabel.length){
34105             this.labelEl.hide();
34106         }
34107         
34108         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34109         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34110         
34111         this.indicator = this.indicatorEl();
34112         
34113         if(this.indicator){
34114             this.indicator.addClass('invisible');
34115         }
34116         
34117         this.originalValue = this.getValue();
34118         
34119     },
34120     
34121     inputEl: function ()
34122     {
34123         return this.el.select('.roo-radio-set-input', true).first();
34124     },
34125     
34126     getChildContainer : function()
34127     {
34128         return this.itemsEl;
34129     },
34130     
34131     register : function(item)
34132     {
34133         this.radioes.push(item);
34134         
34135     },
34136     
34137     validate : function()
34138     {   
34139         if(this.getVisibilityEl().hasClass('hidden')){
34140             return true;
34141         }
34142         
34143         var valid = false;
34144         
34145         Roo.each(this.radioes, function(i){
34146             if(!i.checked){
34147                 return;
34148             }
34149             
34150             valid = true;
34151             return false;
34152         });
34153         
34154         if(this.allowBlank) {
34155             return true;
34156         }
34157         
34158         if(this.disabled || valid){
34159             this.markValid();
34160             return true;
34161         }
34162         
34163         this.markInvalid();
34164         return false;
34165         
34166     },
34167     
34168     markValid : function()
34169     {
34170         if(this.labelEl.isVisible(true)){
34171             this.indicatorEl().removeClass('visible');
34172             this.indicatorEl().addClass('invisible');
34173         }
34174         
34175         this.el.removeClass([this.invalidClass, this.validClass]);
34176         this.el.addClass(this.validClass);
34177         
34178         this.fireEvent('valid', this);
34179     },
34180     
34181     markInvalid : function(msg)
34182     {
34183         if(this.allowBlank || this.disabled){
34184             return;
34185         }
34186         
34187         if(this.labelEl.isVisible(true)){
34188             this.indicatorEl().removeClass('invisible');
34189             this.indicatorEl().addClass('visible');
34190         }
34191         
34192         this.el.removeClass([this.invalidClass, this.validClass]);
34193         this.el.addClass(this.invalidClass);
34194         
34195         this.fireEvent('invalid', this, msg);
34196         
34197     },
34198     
34199     setValue : function(v, suppressEvent)
34200     {   
34201         if(this.value === v){
34202             return;
34203         }
34204         
34205         this.value = v;
34206         
34207         if(this.rendered){
34208             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34209         }
34210         
34211         Roo.each(this.radioes, function(i){
34212             i.checked = false;
34213             i.el.removeClass('checked');
34214         });
34215         
34216         Roo.each(this.radioes, function(i){
34217             
34218             if(i.value === v || i.value.toString() === v.toString()){
34219                 i.checked = true;
34220                 i.el.addClass('checked');
34221                 
34222                 if(suppressEvent !== true){
34223                     this.fireEvent('check', this, i);
34224                 }
34225                 
34226                 return false;
34227             }
34228             
34229         }, this);
34230         
34231         this.validate();
34232     },
34233     
34234     clearInvalid : function(){
34235         
34236         if(!this.el || this.preventMark){
34237             return;
34238         }
34239         
34240         this.el.removeClass([this.invalidClass]);
34241         
34242         this.fireEvent('valid', this);
34243     }
34244     
34245 });
34246
34247 Roo.apply(Roo.bootstrap.RadioSet, {
34248     
34249     groups: {},
34250     
34251     register : function(set)
34252     {
34253         this.groups[set.name] = set;
34254     },
34255     
34256     get: function(name) 
34257     {
34258         if (typeof(this.groups[name]) == 'undefined') {
34259             return false;
34260         }
34261         
34262         return this.groups[name] ;
34263     }
34264     
34265 });
34266 /*
34267  * Based on:
34268  * Ext JS Library 1.1.1
34269  * Copyright(c) 2006-2007, Ext JS, LLC.
34270  *
34271  * Originally Released Under LGPL - original licence link has changed is not relivant.
34272  *
34273  * Fork - LGPL
34274  * <script type="text/javascript">
34275  */
34276
34277
34278 /**
34279  * @class Roo.bootstrap.SplitBar
34280  * @extends Roo.util.Observable
34281  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34282  * <br><br>
34283  * Usage:
34284  * <pre><code>
34285 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34286                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34287 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34288 split.minSize = 100;
34289 split.maxSize = 600;
34290 split.animate = true;
34291 split.on('moved', splitterMoved);
34292 </code></pre>
34293  * @constructor
34294  * Create a new SplitBar
34295  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34296  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34297  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34298  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34299                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34300                         position of the SplitBar).
34301  */
34302 Roo.bootstrap.SplitBar = function(cfg){
34303     
34304     /** @private */
34305     
34306     //{
34307     //  dragElement : elm
34308     //  resizingElement: el,
34309         // optional..
34310     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34311     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34312         // existingProxy ???
34313     //}
34314     
34315     this.el = Roo.get(cfg.dragElement, true);
34316     this.el.dom.unselectable = "on";
34317     /** @private */
34318     this.resizingEl = Roo.get(cfg.resizingElement, true);
34319
34320     /**
34321      * @private
34322      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34323      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34324      * @type Number
34325      */
34326     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34327     
34328     /**
34329      * The minimum size of the resizing element. (Defaults to 0)
34330      * @type Number
34331      */
34332     this.minSize = 0;
34333     
34334     /**
34335      * The maximum size of the resizing element. (Defaults to 2000)
34336      * @type Number
34337      */
34338     this.maxSize = 2000;
34339     
34340     /**
34341      * Whether to animate the transition to the new size
34342      * @type Boolean
34343      */
34344     this.animate = false;
34345     
34346     /**
34347      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34348      * @type Boolean
34349      */
34350     this.useShim = false;
34351     
34352     /** @private */
34353     this.shim = null;
34354     
34355     if(!cfg.existingProxy){
34356         /** @private */
34357         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34358     }else{
34359         this.proxy = Roo.get(cfg.existingProxy).dom;
34360     }
34361     /** @private */
34362     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34363     
34364     /** @private */
34365     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34366     
34367     /** @private */
34368     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34369     
34370     /** @private */
34371     this.dragSpecs = {};
34372     
34373     /**
34374      * @private The adapter to use to positon and resize elements
34375      */
34376     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34377     this.adapter.init(this);
34378     
34379     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34380         /** @private */
34381         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34382         this.el.addClass("roo-splitbar-h");
34383     }else{
34384         /** @private */
34385         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34386         this.el.addClass("roo-splitbar-v");
34387     }
34388     
34389     this.addEvents({
34390         /**
34391          * @event resize
34392          * Fires when the splitter is moved (alias for {@link #event-moved})
34393          * @param {Roo.bootstrap.SplitBar} this
34394          * @param {Number} newSize the new width or height
34395          */
34396         "resize" : true,
34397         /**
34398          * @event moved
34399          * Fires when the splitter is moved
34400          * @param {Roo.bootstrap.SplitBar} this
34401          * @param {Number} newSize the new width or height
34402          */
34403         "moved" : true,
34404         /**
34405          * @event beforeresize
34406          * Fires before the splitter is dragged
34407          * @param {Roo.bootstrap.SplitBar} this
34408          */
34409         "beforeresize" : true,
34410
34411         "beforeapply" : true
34412     });
34413
34414     Roo.util.Observable.call(this);
34415 };
34416
34417 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34418     onStartProxyDrag : function(x, y){
34419         this.fireEvent("beforeresize", this);
34420         if(!this.overlay){
34421             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34422             o.unselectable();
34423             o.enableDisplayMode("block");
34424             // all splitbars share the same overlay
34425             Roo.bootstrap.SplitBar.prototype.overlay = o;
34426         }
34427         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34428         this.overlay.show();
34429         Roo.get(this.proxy).setDisplayed("block");
34430         var size = this.adapter.getElementSize(this);
34431         this.activeMinSize = this.getMinimumSize();;
34432         this.activeMaxSize = this.getMaximumSize();;
34433         var c1 = size - this.activeMinSize;
34434         var c2 = Math.max(this.activeMaxSize - size, 0);
34435         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34436             this.dd.resetConstraints();
34437             this.dd.setXConstraint(
34438                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34439                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34440             );
34441             this.dd.setYConstraint(0, 0);
34442         }else{
34443             this.dd.resetConstraints();
34444             this.dd.setXConstraint(0, 0);
34445             this.dd.setYConstraint(
34446                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34447                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34448             );
34449          }
34450         this.dragSpecs.startSize = size;
34451         this.dragSpecs.startPoint = [x, y];
34452         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34453     },
34454     
34455     /** 
34456      * @private Called after the drag operation by the DDProxy
34457      */
34458     onEndProxyDrag : function(e){
34459         Roo.get(this.proxy).setDisplayed(false);
34460         var endPoint = Roo.lib.Event.getXY(e);
34461         if(this.overlay){
34462             this.overlay.hide();
34463         }
34464         var newSize;
34465         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34466             newSize = this.dragSpecs.startSize + 
34467                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34468                     endPoint[0] - this.dragSpecs.startPoint[0] :
34469                     this.dragSpecs.startPoint[0] - endPoint[0]
34470                 );
34471         }else{
34472             newSize = this.dragSpecs.startSize + 
34473                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34474                     endPoint[1] - this.dragSpecs.startPoint[1] :
34475                     this.dragSpecs.startPoint[1] - endPoint[1]
34476                 );
34477         }
34478         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34479         if(newSize != this.dragSpecs.startSize){
34480             if(this.fireEvent('beforeapply', this, newSize) !== false){
34481                 this.adapter.setElementSize(this, newSize);
34482                 this.fireEvent("moved", this, newSize);
34483                 this.fireEvent("resize", this, newSize);
34484             }
34485         }
34486     },
34487     
34488     /**
34489      * Get the adapter this SplitBar uses
34490      * @return The adapter object
34491      */
34492     getAdapter : function(){
34493         return this.adapter;
34494     },
34495     
34496     /**
34497      * Set the adapter this SplitBar uses
34498      * @param {Object} adapter A SplitBar adapter object
34499      */
34500     setAdapter : function(adapter){
34501         this.adapter = adapter;
34502         this.adapter.init(this);
34503     },
34504     
34505     /**
34506      * Gets the minimum size for the resizing element
34507      * @return {Number} The minimum size
34508      */
34509     getMinimumSize : function(){
34510         return this.minSize;
34511     },
34512     
34513     /**
34514      * Sets the minimum size for the resizing element
34515      * @param {Number} minSize The minimum size
34516      */
34517     setMinimumSize : function(minSize){
34518         this.minSize = minSize;
34519     },
34520     
34521     /**
34522      * Gets the maximum size for the resizing element
34523      * @return {Number} The maximum size
34524      */
34525     getMaximumSize : function(){
34526         return this.maxSize;
34527     },
34528     
34529     /**
34530      * Sets the maximum size for the resizing element
34531      * @param {Number} maxSize The maximum size
34532      */
34533     setMaximumSize : function(maxSize){
34534         this.maxSize = maxSize;
34535     },
34536     
34537     /**
34538      * Sets the initialize size for the resizing element
34539      * @param {Number} size The initial size
34540      */
34541     setCurrentSize : function(size){
34542         var oldAnimate = this.animate;
34543         this.animate = false;
34544         this.adapter.setElementSize(this, size);
34545         this.animate = oldAnimate;
34546     },
34547     
34548     /**
34549      * Destroy this splitbar. 
34550      * @param {Boolean} removeEl True to remove the element
34551      */
34552     destroy : function(removeEl){
34553         if(this.shim){
34554             this.shim.remove();
34555         }
34556         this.dd.unreg();
34557         this.proxy.parentNode.removeChild(this.proxy);
34558         if(removeEl){
34559             this.el.remove();
34560         }
34561     }
34562 });
34563
34564 /**
34565  * @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.
34566  */
34567 Roo.bootstrap.SplitBar.createProxy = function(dir){
34568     var proxy = new Roo.Element(document.createElement("div"));
34569     proxy.unselectable();
34570     var cls = 'roo-splitbar-proxy';
34571     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34572     document.body.appendChild(proxy.dom);
34573     return proxy.dom;
34574 };
34575
34576 /** 
34577  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34578  * Default Adapter. It assumes the splitter and resizing element are not positioned
34579  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34580  */
34581 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34582 };
34583
34584 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34585     // do nothing for now
34586     init : function(s){
34587     
34588     },
34589     /**
34590      * Called before drag operations to get the current size of the resizing element. 
34591      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34592      */
34593      getElementSize : function(s){
34594         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34595             return s.resizingEl.getWidth();
34596         }else{
34597             return s.resizingEl.getHeight();
34598         }
34599     },
34600     
34601     /**
34602      * Called after drag operations to set the size of the resizing element.
34603      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34604      * @param {Number} newSize The new size to set
34605      * @param {Function} onComplete A function to be invoked when resizing is complete
34606      */
34607     setElementSize : function(s, newSize, onComplete){
34608         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34609             if(!s.animate){
34610                 s.resizingEl.setWidth(newSize);
34611                 if(onComplete){
34612                     onComplete(s, newSize);
34613                 }
34614             }else{
34615                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34616             }
34617         }else{
34618             
34619             if(!s.animate){
34620                 s.resizingEl.setHeight(newSize);
34621                 if(onComplete){
34622                     onComplete(s, newSize);
34623                 }
34624             }else{
34625                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34626             }
34627         }
34628     }
34629 };
34630
34631 /** 
34632  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34633  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34634  * Adapter that  moves the splitter element to align with the resized sizing element. 
34635  * Used with an absolute positioned SplitBar.
34636  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34637  * document.body, make sure you assign an id to the body element.
34638  */
34639 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34640     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34641     this.container = Roo.get(container);
34642 };
34643
34644 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34645     init : function(s){
34646         this.basic.init(s);
34647     },
34648     
34649     getElementSize : function(s){
34650         return this.basic.getElementSize(s);
34651     },
34652     
34653     setElementSize : function(s, newSize, onComplete){
34654         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34655     },
34656     
34657     moveSplitter : function(s){
34658         var yes = Roo.bootstrap.SplitBar;
34659         switch(s.placement){
34660             case yes.LEFT:
34661                 s.el.setX(s.resizingEl.getRight());
34662                 break;
34663             case yes.RIGHT:
34664                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34665                 break;
34666             case yes.TOP:
34667                 s.el.setY(s.resizingEl.getBottom());
34668                 break;
34669             case yes.BOTTOM:
34670                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34671                 break;
34672         }
34673     }
34674 };
34675
34676 /**
34677  * Orientation constant - Create a vertical SplitBar
34678  * @static
34679  * @type Number
34680  */
34681 Roo.bootstrap.SplitBar.VERTICAL = 1;
34682
34683 /**
34684  * Orientation constant - Create a horizontal SplitBar
34685  * @static
34686  * @type Number
34687  */
34688 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34689
34690 /**
34691  * Placement constant - The resizing element is to the left of the splitter element
34692  * @static
34693  * @type Number
34694  */
34695 Roo.bootstrap.SplitBar.LEFT = 1;
34696
34697 /**
34698  * Placement constant - The resizing element is to the right of the splitter element
34699  * @static
34700  * @type Number
34701  */
34702 Roo.bootstrap.SplitBar.RIGHT = 2;
34703
34704 /**
34705  * Placement constant - The resizing element is positioned above the splitter element
34706  * @static
34707  * @type Number
34708  */
34709 Roo.bootstrap.SplitBar.TOP = 3;
34710
34711 /**
34712  * Placement constant - The resizing element is positioned under splitter element
34713  * @static
34714  * @type Number
34715  */
34716 Roo.bootstrap.SplitBar.BOTTOM = 4;
34717 Roo.namespace("Roo.bootstrap.layout");/*
34718  * Based on:
34719  * Ext JS Library 1.1.1
34720  * Copyright(c) 2006-2007, Ext JS, LLC.
34721  *
34722  * Originally Released Under LGPL - original licence link has changed is not relivant.
34723  *
34724  * Fork - LGPL
34725  * <script type="text/javascript">
34726  */
34727
34728 /**
34729  * @class Roo.bootstrap.layout.Manager
34730  * @extends Roo.bootstrap.Component
34731  * Base class for layout managers.
34732  */
34733 Roo.bootstrap.layout.Manager = function(config)
34734 {
34735     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34736
34737
34738
34739
34740
34741     /** false to disable window resize monitoring @type Boolean */
34742     this.monitorWindowResize = true;
34743     this.regions = {};
34744     this.addEvents({
34745         /**
34746          * @event layout
34747          * Fires when a layout is performed.
34748          * @param {Roo.LayoutManager} this
34749          */
34750         "layout" : true,
34751         /**
34752          * @event regionresized
34753          * Fires when the user resizes a region.
34754          * @param {Roo.LayoutRegion} region The resized region
34755          * @param {Number} newSize The new size (width for east/west, height for north/south)
34756          */
34757         "regionresized" : true,
34758         /**
34759          * @event regioncollapsed
34760          * Fires when a region is collapsed.
34761          * @param {Roo.LayoutRegion} region The collapsed region
34762          */
34763         "regioncollapsed" : true,
34764         /**
34765          * @event regionexpanded
34766          * Fires when a region is expanded.
34767          * @param {Roo.LayoutRegion} region The expanded region
34768          */
34769         "regionexpanded" : true
34770     });
34771     this.updating = false;
34772
34773     if (config.el) {
34774         this.el = Roo.get(config.el);
34775         this.initEvents();
34776     }
34777
34778 };
34779
34780 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34781
34782
34783     regions : null,
34784
34785     monitorWindowResize : true,
34786
34787
34788     updating : false,
34789
34790
34791     onRender : function(ct, position)
34792     {
34793         if(!this.el){
34794             this.el = Roo.get(ct);
34795             this.initEvents();
34796         }
34797         //this.fireEvent('render',this);
34798     },
34799
34800
34801     initEvents: function()
34802     {
34803
34804
34805         // ie scrollbar fix
34806         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34807             document.body.scroll = "no";
34808         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34809             this.el.position('relative');
34810         }
34811         this.id = this.el.id;
34812         this.el.addClass("roo-layout-container");
34813         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34814         if(this.el.dom != document.body ) {
34815             this.el.on('resize', this.layout,this);
34816             this.el.on('show', this.layout,this);
34817         }
34818
34819     },
34820
34821     /**
34822      * Returns true if this layout is currently being updated
34823      * @return {Boolean}
34824      */
34825     isUpdating : function(){
34826         return this.updating;
34827     },
34828
34829     /**
34830      * Suspend the LayoutManager from doing auto-layouts while
34831      * making multiple add or remove calls
34832      */
34833     beginUpdate : function(){
34834         this.updating = true;
34835     },
34836
34837     /**
34838      * Restore auto-layouts and optionally disable the manager from performing a layout
34839      * @param {Boolean} noLayout true to disable a layout update
34840      */
34841     endUpdate : function(noLayout){
34842         this.updating = false;
34843         if(!noLayout){
34844             this.layout();
34845         }
34846     },
34847
34848     layout: function(){
34849         // abstract...
34850     },
34851
34852     onRegionResized : function(region, newSize){
34853         this.fireEvent("regionresized", region, newSize);
34854         this.layout();
34855     },
34856
34857     onRegionCollapsed : function(region){
34858         this.fireEvent("regioncollapsed", region);
34859     },
34860
34861     onRegionExpanded : function(region){
34862         this.fireEvent("regionexpanded", region);
34863     },
34864
34865     /**
34866      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34867      * performs box-model adjustments.
34868      * @return {Object} The size as an object {width: (the width), height: (the height)}
34869      */
34870     getViewSize : function()
34871     {
34872         var size;
34873         if(this.el.dom != document.body){
34874             size = this.el.getSize();
34875         }else{
34876             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34877         }
34878         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34879         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34880         return size;
34881     },
34882
34883     /**
34884      * Returns the Element this layout is bound to.
34885      * @return {Roo.Element}
34886      */
34887     getEl : function(){
34888         return this.el;
34889     },
34890
34891     /**
34892      * Returns the specified region.
34893      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34894      * @return {Roo.LayoutRegion}
34895      */
34896     getRegion : function(target){
34897         return this.regions[target.toLowerCase()];
34898     },
34899
34900     onWindowResize : function(){
34901         if(this.monitorWindowResize){
34902             this.layout();
34903         }
34904     }
34905 });
34906 /*
34907  * Based on:
34908  * Ext JS Library 1.1.1
34909  * Copyright(c) 2006-2007, Ext JS, LLC.
34910  *
34911  * Originally Released Under LGPL - original licence link has changed is not relivant.
34912  *
34913  * Fork - LGPL
34914  * <script type="text/javascript">
34915  */
34916 /**
34917  * @class Roo.bootstrap.layout.Border
34918  * @extends Roo.bootstrap.layout.Manager
34919  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34920  * please see: examples/bootstrap/nested.html<br><br>
34921  
34922 <b>The container the layout is rendered into can be either the body element or any other element.
34923 If it is not the body element, the container needs to either be an absolute positioned element,
34924 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34925 the container size if it is not the body element.</b>
34926
34927 * @constructor
34928 * Create a new Border
34929 * @param {Object} config Configuration options
34930  */
34931 Roo.bootstrap.layout.Border = function(config){
34932     config = config || {};
34933     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34934     
34935     
34936     
34937     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34938         if(config[region]){
34939             config[region].region = region;
34940             this.addRegion(config[region]);
34941         }
34942     },this);
34943     
34944 };
34945
34946 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34947
34948 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34949     /**
34950      * Creates and adds a new region if it doesn't already exist.
34951      * @param {String} target The target region key (north, south, east, west or center).
34952      * @param {Object} config The regions config object
34953      * @return {BorderLayoutRegion} The new region
34954      */
34955     addRegion : function(config)
34956     {
34957         if(!this.regions[config.region]){
34958             var r = this.factory(config);
34959             this.bindRegion(r);
34960         }
34961         return this.regions[config.region];
34962     },
34963
34964     // private (kinda)
34965     bindRegion : function(r){
34966         this.regions[r.config.region] = r;
34967         
34968         r.on("visibilitychange",    this.layout, this);
34969         r.on("paneladded",          this.layout, this);
34970         r.on("panelremoved",        this.layout, this);
34971         r.on("invalidated",         this.layout, this);
34972         r.on("resized",             this.onRegionResized, this);
34973         r.on("collapsed",           this.onRegionCollapsed, this);
34974         r.on("expanded",            this.onRegionExpanded, this);
34975     },
34976
34977     /**
34978      * Performs a layout update.
34979      */
34980     layout : function()
34981     {
34982         if(this.updating) {
34983             return;
34984         }
34985         
34986         // render all the rebions if they have not been done alreayd?
34987         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34988             if(this.regions[region] && !this.regions[region].bodyEl){
34989                 this.regions[region].onRender(this.el)
34990             }
34991         },this);
34992         
34993         var size = this.getViewSize();
34994         var w = size.width;
34995         var h = size.height;
34996         var centerW = w;
34997         var centerH = h;
34998         var centerY = 0;
34999         var centerX = 0;
35000         //var x = 0, y = 0;
35001
35002         var rs = this.regions;
35003         var north = rs["north"];
35004         var south = rs["south"]; 
35005         var west = rs["west"];
35006         var east = rs["east"];
35007         var center = rs["center"];
35008         //if(this.hideOnLayout){ // not supported anymore
35009             //c.el.setStyle("display", "none");
35010         //}
35011         if(north && north.isVisible()){
35012             var b = north.getBox();
35013             var m = north.getMargins();
35014             b.width = w - (m.left+m.right);
35015             b.x = m.left;
35016             b.y = m.top;
35017             centerY = b.height + b.y + m.bottom;
35018             centerH -= centerY;
35019             north.updateBox(this.safeBox(b));
35020         }
35021         if(south && south.isVisible()){
35022             var b = south.getBox();
35023             var m = south.getMargins();
35024             b.width = w - (m.left+m.right);
35025             b.x = m.left;
35026             var totalHeight = (b.height + m.top + m.bottom);
35027             b.y = h - totalHeight + m.top;
35028             centerH -= totalHeight;
35029             south.updateBox(this.safeBox(b));
35030         }
35031         if(west && west.isVisible()){
35032             var b = west.getBox();
35033             var m = west.getMargins();
35034             b.height = centerH - (m.top+m.bottom);
35035             b.x = m.left;
35036             b.y = centerY + m.top;
35037             var totalWidth = (b.width + m.left + m.right);
35038             centerX += totalWidth;
35039             centerW -= totalWidth;
35040             west.updateBox(this.safeBox(b));
35041         }
35042         if(east && east.isVisible()){
35043             var b = east.getBox();
35044             var m = east.getMargins();
35045             b.height = centerH - (m.top+m.bottom);
35046             var totalWidth = (b.width + m.left + m.right);
35047             b.x = w - totalWidth + m.left;
35048             b.y = centerY + m.top;
35049             centerW -= totalWidth;
35050             east.updateBox(this.safeBox(b));
35051         }
35052         if(center){
35053             var m = center.getMargins();
35054             var centerBox = {
35055                 x: centerX + m.left,
35056                 y: centerY + m.top,
35057                 width: centerW - (m.left+m.right),
35058                 height: centerH - (m.top+m.bottom)
35059             };
35060             //if(this.hideOnLayout){
35061                 //center.el.setStyle("display", "block");
35062             //}
35063             center.updateBox(this.safeBox(centerBox));
35064         }
35065         this.el.repaint();
35066         this.fireEvent("layout", this);
35067     },
35068
35069     // private
35070     safeBox : function(box){
35071         box.width = Math.max(0, box.width);
35072         box.height = Math.max(0, box.height);
35073         return box;
35074     },
35075
35076     /**
35077      * Adds a ContentPanel (or subclass) to this layout.
35078      * @param {String} target The target region key (north, south, east, west or center).
35079      * @param {Roo.ContentPanel} panel The panel to add
35080      * @return {Roo.ContentPanel} The added panel
35081      */
35082     add : function(target, panel){
35083          
35084         target = target.toLowerCase();
35085         return this.regions[target].add(panel);
35086     },
35087
35088     /**
35089      * Remove a ContentPanel (or subclass) to this layout.
35090      * @param {String} target The target region key (north, south, east, west or center).
35091      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35092      * @return {Roo.ContentPanel} The removed panel
35093      */
35094     remove : function(target, panel){
35095         target = target.toLowerCase();
35096         return this.regions[target].remove(panel);
35097     },
35098
35099     /**
35100      * Searches all regions for a panel with the specified id
35101      * @param {String} panelId
35102      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35103      */
35104     findPanel : function(panelId){
35105         var rs = this.regions;
35106         for(var target in rs){
35107             if(typeof rs[target] != "function"){
35108                 var p = rs[target].getPanel(panelId);
35109                 if(p){
35110                     return p;
35111                 }
35112             }
35113         }
35114         return null;
35115     },
35116
35117     /**
35118      * Searches all regions for a panel with the specified id and activates (shows) it.
35119      * @param {String/ContentPanel} panelId The panels id or the panel itself
35120      * @return {Roo.ContentPanel} The shown panel or null
35121      */
35122     showPanel : function(panelId) {
35123       var rs = this.regions;
35124       for(var target in rs){
35125          var r = rs[target];
35126          if(typeof r != "function"){
35127             if(r.hasPanel(panelId)){
35128                return r.showPanel(panelId);
35129             }
35130          }
35131       }
35132       return null;
35133    },
35134
35135    /**
35136      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35137      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35138      */
35139    /*
35140     restoreState : function(provider){
35141         if(!provider){
35142             provider = Roo.state.Manager;
35143         }
35144         var sm = new Roo.LayoutStateManager();
35145         sm.init(this, provider);
35146     },
35147 */
35148  
35149  
35150     /**
35151      * Adds a xtype elements to the layout.
35152      * <pre><code>
35153
35154 layout.addxtype({
35155        xtype : 'ContentPanel',
35156        region: 'west',
35157        items: [ .... ]
35158    }
35159 );
35160
35161 layout.addxtype({
35162         xtype : 'NestedLayoutPanel',
35163         region: 'west',
35164         layout: {
35165            center: { },
35166            west: { }   
35167         },
35168         items : [ ... list of content panels or nested layout panels.. ]
35169    }
35170 );
35171 </code></pre>
35172      * @param {Object} cfg Xtype definition of item to add.
35173      */
35174     addxtype : function(cfg)
35175     {
35176         // basically accepts a pannel...
35177         // can accept a layout region..!?!?
35178         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35179         
35180         
35181         // theory?  children can only be panels??
35182         
35183         //if (!cfg.xtype.match(/Panel$/)) {
35184         //    return false;
35185         //}
35186         var ret = false;
35187         
35188         if (typeof(cfg.region) == 'undefined') {
35189             Roo.log("Failed to add Panel, region was not set");
35190             Roo.log(cfg);
35191             return false;
35192         }
35193         var region = cfg.region;
35194         delete cfg.region;
35195         
35196           
35197         var xitems = [];
35198         if (cfg.items) {
35199             xitems = cfg.items;
35200             delete cfg.items;
35201         }
35202         var nb = false;
35203         
35204         switch(cfg.xtype) 
35205         {
35206             case 'Content':  // ContentPanel (el, cfg)
35207             case 'Scroll':  // ContentPanel (el, cfg)
35208             case 'View': 
35209                 cfg.autoCreate = true;
35210                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35211                 //} else {
35212                 //    var el = this.el.createChild();
35213                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35214                 //}
35215                 
35216                 this.add(region, ret);
35217                 break;
35218             
35219             /*
35220             case 'TreePanel': // our new panel!
35221                 cfg.el = this.el.createChild();
35222                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35223                 this.add(region, ret);
35224                 break;
35225             */
35226             
35227             case 'Nest': 
35228                 // create a new Layout (which is  a Border Layout...
35229                 
35230                 var clayout = cfg.layout;
35231                 clayout.el  = this.el.createChild();
35232                 clayout.items   = clayout.items  || [];
35233                 
35234                 delete cfg.layout;
35235                 
35236                 // replace this exitems with the clayout ones..
35237                 xitems = clayout.items;
35238                  
35239                 // force background off if it's in center...
35240                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35241                     cfg.background = false;
35242                 }
35243                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35244                 
35245                 
35246                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35247                 //console.log('adding nested layout panel '  + cfg.toSource());
35248                 this.add(region, ret);
35249                 nb = {}; /// find first...
35250                 break;
35251             
35252             case 'Grid':
35253                 
35254                 // needs grid and region
35255                 
35256                 //var el = this.getRegion(region).el.createChild();
35257                 /*
35258                  *var el = this.el.createChild();
35259                 // create the grid first...
35260                 cfg.grid.container = el;
35261                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35262                 */
35263                 
35264                 if (region == 'center' && this.active ) {
35265                     cfg.background = false;
35266                 }
35267                 
35268                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35269                 
35270                 this.add(region, ret);
35271                 /*
35272                 if (cfg.background) {
35273                     // render grid on panel activation (if panel background)
35274                     ret.on('activate', function(gp) {
35275                         if (!gp.grid.rendered) {
35276                     //        gp.grid.render(el);
35277                         }
35278                     });
35279                 } else {
35280                   //  cfg.grid.render(el);
35281                 }
35282                 */
35283                 break;
35284            
35285            
35286             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35287                 // it was the old xcomponent building that caused this before.
35288                 // espeically if border is the top element in the tree.
35289                 ret = this;
35290                 break; 
35291                 
35292                     
35293                 
35294                 
35295                 
35296             default:
35297                 /*
35298                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35299                     
35300                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35301                     this.add(region, ret);
35302                 } else {
35303                 */
35304                     Roo.log(cfg);
35305                     throw "Can not add '" + cfg.xtype + "' to Border";
35306                     return null;
35307              
35308                                 
35309              
35310         }
35311         this.beginUpdate();
35312         // add children..
35313         var region = '';
35314         var abn = {};
35315         Roo.each(xitems, function(i)  {
35316             region = nb && i.region ? i.region : false;
35317             
35318             var add = ret.addxtype(i);
35319            
35320             if (region) {
35321                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35322                 if (!i.background) {
35323                     abn[region] = nb[region] ;
35324                 }
35325             }
35326             
35327         });
35328         this.endUpdate();
35329
35330         // make the last non-background panel active..
35331         //if (nb) { Roo.log(abn); }
35332         if (nb) {
35333             
35334             for(var r in abn) {
35335                 region = this.getRegion(r);
35336                 if (region) {
35337                     // tried using nb[r], but it does not work..
35338                      
35339                     region.showPanel(abn[r]);
35340                    
35341                 }
35342             }
35343         }
35344         return ret;
35345         
35346     },
35347     
35348     
35349 // private
35350     factory : function(cfg)
35351     {
35352         
35353         var validRegions = Roo.bootstrap.layout.Border.regions;
35354
35355         var target = cfg.region;
35356         cfg.mgr = this;
35357         
35358         var r = Roo.bootstrap.layout;
35359         Roo.log(target);
35360         switch(target){
35361             case "north":
35362                 return new r.North(cfg);
35363             case "south":
35364                 return new r.South(cfg);
35365             case "east":
35366                 return new r.East(cfg);
35367             case "west":
35368                 return new r.West(cfg);
35369             case "center":
35370                 return new r.Center(cfg);
35371         }
35372         throw 'Layout region "'+target+'" not supported.';
35373     }
35374     
35375     
35376 });
35377  /*
35378  * Based on:
35379  * Ext JS Library 1.1.1
35380  * Copyright(c) 2006-2007, Ext JS, LLC.
35381  *
35382  * Originally Released Under LGPL - original licence link has changed is not relivant.
35383  *
35384  * Fork - LGPL
35385  * <script type="text/javascript">
35386  */
35387  
35388 /**
35389  * @class Roo.bootstrap.layout.Basic
35390  * @extends Roo.util.Observable
35391  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35392  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35393  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35394  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35395  * @cfg {string}   region  the region that it inhabits..
35396  * @cfg {bool}   skipConfig skip config?
35397  * 
35398
35399  */
35400 Roo.bootstrap.layout.Basic = function(config){
35401     
35402     this.mgr = config.mgr;
35403     
35404     this.position = config.region;
35405     
35406     var skipConfig = config.skipConfig;
35407     
35408     this.events = {
35409         /**
35410          * @scope Roo.BasicLayoutRegion
35411          */
35412         
35413         /**
35414          * @event beforeremove
35415          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35416          * @param {Roo.LayoutRegion} this
35417          * @param {Roo.ContentPanel} panel The panel
35418          * @param {Object} e The cancel event object
35419          */
35420         "beforeremove" : true,
35421         /**
35422          * @event invalidated
35423          * Fires when the layout for this region is changed.
35424          * @param {Roo.LayoutRegion} this
35425          */
35426         "invalidated" : true,
35427         /**
35428          * @event visibilitychange
35429          * Fires when this region is shown or hidden 
35430          * @param {Roo.LayoutRegion} this
35431          * @param {Boolean} visibility true or false
35432          */
35433         "visibilitychange" : true,
35434         /**
35435          * @event paneladded
35436          * Fires when a panel is added. 
35437          * @param {Roo.LayoutRegion} this
35438          * @param {Roo.ContentPanel} panel The panel
35439          */
35440         "paneladded" : true,
35441         /**
35442          * @event panelremoved
35443          * Fires when a panel is removed. 
35444          * @param {Roo.LayoutRegion} this
35445          * @param {Roo.ContentPanel} panel The panel
35446          */
35447         "panelremoved" : true,
35448         /**
35449          * @event beforecollapse
35450          * Fires when this region before collapse.
35451          * @param {Roo.LayoutRegion} this
35452          */
35453         "beforecollapse" : true,
35454         /**
35455          * @event collapsed
35456          * Fires when this region is collapsed.
35457          * @param {Roo.LayoutRegion} this
35458          */
35459         "collapsed" : true,
35460         /**
35461          * @event expanded
35462          * Fires when this region is expanded.
35463          * @param {Roo.LayoutRegion} this
35464          */
35465         "expanded" : true,
35466         /**
35467          * @event slideshow
35468          * Fires when this region is slid into view.
35469          * @param {Roo.LayoutRegion} this
35470          */
35471         "slideshow" : true,
35472         /**
35473          * @event slidehide
35474          * Fires when this region slides out of view. 
35475          * @param {Roo.LayoutRegion} this
35476          */
35477         "slidehide" : true,
35478         /**
35479          * @event panelactivated
35480          * Fires when a panel is activated. 
35481          * @param {Roo.LayoutRegion} this
35482          * @param {Roo.ContentPanel} panel The activated panel
35483          */
35484         "panelactivated" : true,
35485         /**
35486          * @event resized
35487          * Fires when the user resizes this region. 
35488          * @param {Roo.LayoutRegion} this
35489          * @param {Number} newSize The new size (width for east/west, height for north/south)
35490          */
35491         "resized" : true
35492     };
35493     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35494     this.panels = new Roo.util.MixedCollection();
35495     this.panels.getKey = this.getPanelId.createDelegate(this);
35496     this.box = null;
35497     this.activePanel = null;
35498     // ensure listeners are added...
35499     
35500     if (config.listeners || config.events) {
35501         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35502             listeners : config.listeners || {},
35503             events : config.events || {}
35504         });
35505     }
35506     
35507     if(skipConfig !== true){
35508         this.applyConfig(config);
35509     }
35510 };
35511
35512 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35513 {
35514     getPanelId : function(p){
35515         return p.getId();
35516     },
35517     
35518     applyConfig : function(config){
35519         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35520         this.config = config;
35521         
35522     },
35523     
35524     /**
35525      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35526      * the width, for horizontal (north, south) the height.
35527      * @param {Number} newSize The new width or height
35528      */
35529     resizeTo : function(newSize){
35530         var el = this.el ? this.el :
35531                  (this.activePanel ? this.activePanel.getEl() : null);
35532         if(el){
35533             switch(this.position){
35534                 case "east":
35535                 case "west":
35536                     el.setWidth(newSize);
35537                     this.fireEvent("resized", this, newSize);
35538                 break;
35539                 case "north":
35540                 case "south":
35541                     el.setHeight(newSize);
35542                     this.fireEvent("resized", this, newSize);
35543                 break;                
35544             }
35545         }
35546     },
35547     
35548     getBox : function(){
35549         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35550     },
35551     
35552     getMargins : function(){
35553         return this.margins;
35554     },
35555     
35556     updateBox : function(box){
35557         this.box = box;
35558         var el = this.activePanel.getEl();
35559         el.dom.style.left = box.x + "px";
35560         el.dom.style.top = box.y + "px";
35561         this.activePanel.setSize(box.width, box.height);
35562     },
35563     
35564     /**
35565      * Returns the container element for this region.
35566      * @return {Roo.Element}
35567      */
35568     getEl : function(){
35569         return this.activePanel;
35570     },
35571     
35572     /**
35573      * Returns true if this region is currently visible.
35574      * @return {Boolean}
35575      */
35576     isVisible : function(){
35577         return this.activePanel ? true : false;
35578     },
35579     
35580     setActivePanel : function(panel){
35581         panel = this.getPanel(panel);
35582         if(this.activePanel && this.activePanel != panel){
35583             this.activePanel.setActiveState(false);
35584             this.activePanel.getEl().setLeftTop(-10000,-10000);
35585         }
35586         this.activePanel = panel;
35587         panel.setActiveState(true);
35588         if(this.box){
35589             panel.setSize(this.box.width, this.box.height);
35590         }
35591         this.fireEvent("panelactivated", this, panel);
35592         this.fireEvent("invalidated");
35593     },
35594     
35595     /**
35596      * Show the specified panel.
35597      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35598      * @return {Roo.ContentPanel} The shown panel or null
35599      */
35600     showPanel : function(panel){
35601         panel = this.getPanel(panel);
35602         if(panel){
35603             this.setActivePanel(panel);
35604         }
35605         return panel;
35606     },
35607     
35608     /**
35609      * Get the active panel for this region.
35610      * @return {Roo.ContentPanel} The active panel or null
35611      */
35612     getActivePanel : function(){
35613         return this.activePanel;
35614     },
35615     
35616     /**
35617      * Add the passed ContentPanel(s)
35618      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35619      * @return {Roo.ContentPanel} The panel added (if only one was added)
35620      */
35621     add : function(panel){
35622         if(arguments.length > 1){
35623             for(var i = 0, len = arguments.length; i < len; i++) {
35624                 this.add(arguments[i]);
35625             }
35626             return null;
35627         }
35628         if(this.hasPanel(panel)){
35629             this.showPanel(panel);
35630             return panel;
35631         }
35632         var el = panel.getEl();
35633         if(el.dom.parentNode != this.mgr.el.dom){
35634             this.mgr.el.dom.appendChild(el.dom);
35635         }
35636         if(panel.setRegion){
35637             panel.setRegion(this);
35638         }
35639         this.panels.add(panel);
35640         el.setStyle("position", "absolute");
35641         if(!panel.background){
35642             this.setActivePanel(panel);
35643             if(this.config.initialSize && this.panels.getCount()==1){
35644                 this.resizeTo(this.config.initialSize);
35645             }
35646         }
35647         this.fireEvent("paneladded", this, panel);
35648         return panel;
35649     },
35650     
35651     /**
35652      * Returns true if the panel is in this region.
35653      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35654      * @return {Boolean}
35655      */
35656     hasPanel : function(panel){
35657         if(typeof panel == "object"){ // must be panel obj
35658             panel = panel.getId();
35659         }
35660         return this.getPanel(panel) ? true : false;
35661     },
35662     
35663     /**
35664      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35665      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35666      * @param {Boolean} preservePanel Overrides the config preservePanel option
35667      * @return {Roo.ContentPanel} The panel that was removed
35668      */
35669     remove : function(panel, preservePanel){
35670         panel = this.getPanel(panel);
35671         if(!panel){
35672             return null;
35673         }
35674         var e = {};
35675         this.fireEvent("beforeremove", this, panel, e);
35676         if(e.cancel === true){
35677             return null;
35678         }
35679         var panelId = panel.getId();
35680         this.panels.removeKey(panelId);
35681         return panel;
35682     },
35683     
35684     /**
35685      * Returns the panel specified or null if it's not in this region.
35686      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35687      * @return {Roo.ContentPanel}
35688      */
35689     getPanel : function(id){
35690         if(typeof id == "object"){ // must be panel obj
35691             return id;
35692         }
35693         return this.panels.get(id);
35694     },
35695     
35696     /**
35697      * Returns this regions position (north/south/east/west/center).
35698      * @return {String} 
35699      */
35700     getPosition: function(){
35701         return this.position;    
35702     }
35703 });/*
35704  * Based on:
35705  * Ext JS Library 1.1.1
35706  * Copyright(c) 2006-2007, Ext JS, LLC.
35707  *
35708  * Originally Released Under LGPL - original licence link has changed is not relivant.
35709  *
35710  * Fork - LGPL
35711  * <script type="text/javascript">
35712  */
35713  
35714 /**
35715  * @class Roo.bootstrap.layout.Region
35716  * @extends Roo.bootstrap.layout.Basic
35717  * This class represents a region in a layout manager.
35718  
35719  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35720  * @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})
35721  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35722  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35723  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35724  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35725  * @cfg {String}    title           The title for the region (overrides panel titles)
35726  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35727  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35728  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35729  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35730  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35731  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35732  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35733  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35734  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35735  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35736
35737  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35738  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35739  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35740  * @cfg {Number}    width           For East/West panels
35741  * @cfg {Number}    height          For North/South panels
35742  * @cfg {Boolean}   split           To show the splitter
35743  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35744  * 
35745  * @cfg {string}   cls             Extra CSS classes to add to region
35746  * 
35747  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35748  * @cfg {string}   region  the region that it inhabits..
35749  *
35750
35751  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35752  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35753
35754  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35755  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35756  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35757  */
35758 Roo.bootstrap.layout.Region = function(config)
35759 {
35760     this.applyConfig(config);
35761
35762     var mgr = config.mgr;
35763     var pos = config.region;
35764     config.skipConfig = true;
35765     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35766     
35767     if (mgr.el) {
35768         this.onRender(mgr.el);   
35769     }
35770      
35771     this.visible = true;
35772     this.collapsed = false;
35773     this.unrendered_panels = [];
35774 };
35775
35776 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35777
35778     position: '', // set by wrapper (eg. north/south etc..)
35779     unrendered_panels : null,  // unrendered panels.
35780     createBody : function(){
35781         /** This region's body element 
35782         * @type Roo.Element */
35783         this.bodyEl = this.el.createChild({
35784                 tag: "div",
35785                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35786         });
35787     },
35788
35789     onRender: function(ctr, pos)
35790     {
35791         var dh = Roo.DomHelper;
35792         /** This region's container element 
35793         * @type Roo.Element */
35794         this.el = dh.append(ctr.dom, {
35795                 tag: "div",
35796                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35797             }, true);
35798         /** This region's title element 
35799         * @type Roo.Element */
35800     
35801         this.titleEl = dh.append(this.el.dom,
35802             {
35803                     tag: "div",
35804                     unselectable: "on",
35805                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35806                     children:[
35807                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35808                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35809                     ]}, true);
35810         
35811         this.titleEl.enableDisplayMode();
35812         /** This region's title text element 
35813         * @type HTMLElement */
35814         this.titleTextEl = this.titleEl.dom.firstChild;
35815         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35816         /*
35817         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35818         this.closeBtn.enableDisplayMode();
35819         this.closeBtn.on("click", this.closeClicked, this);
35820         this.closeBtn.hide();
35821     */
35822         this.createBody(this.config);
35823         if(this.config.hideWhenEmpty){
35824             this.hide();
35825             this.on("paneladded", this.validateVisibility, this);
35826             this.on("panelremoved", this.validateVisibility, this);
35827         }
35828         if(this.autoScroll){
35829             this.bodyEl.setStyle("overflow", "auto");
35830         }else{
35831             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35832         }
35833         //if(c.titlebar !== false){
35834             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35835                 this.titleEl.hide();
35836             }else{
35837                 this.titleEl.show();
35838                 if(this.config.title){
35839                     this.titleTextEl.innerHTML = this.config.title;
35840                 }
35841             }
35842         //}
35843         if(this.config.collapsed){
35844             this.collapse(true);
35845         }
35846         if(this.config.hidden){
35847             this.hide();
35848         }
35849         
35850         if (this.unrendered_panels && this.unrendered_panels.length) {
35851             for (var i =0;i< this.unrendered_panels.length; i++) {
35852                 this.add(this.unrendered_panels[i]);
35853             }
35854             this.unrendered_panels = null;
35855             
35856         }
35857         
35858     },
35859     
35860     applyConfig : function(c)
35861     {
35862         /*
35863          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35864             var dh = Roo.DomHelper;
35865             if(c.titlebar !== false){
35866                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35867                 this.collapseBtn.on("click", this.collapse, this);
35868                 this.collapseBtn.enableDisplayMode();
35869                 /*
35870                 if(c.showPin === true || this.showPin){
35871                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35872                     this.stickBtn.enableDisplayMode();
35873                     this.stickBtn.on("click", this.expand, this);
35874                     this.stickBtn.hide();
35875                 }
35876                 
35877             }
35878             */
35879             /** This region's collapsed element
35880             * @type Roo.Element */
35881             /*
35882              *
35883             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35884                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35885             ]}, true);
35886             
35887             if(c.floatable !== false){
35888                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35889                this.collapsedEl.on("click", this.collapseClick, this);
35890             }
35891
35892             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35893                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35894                    id: "message", unselectable: "on", style:{"float":"left"}});
35895                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35896              }
35897             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35898             this.expandBtn.on("click", this.expand, this);
35899             
35900         }
35901         
35902         if(this.collapseBtn){
35903             this.collapseBtn.setVisible(c.collapsible == true);
35904         }
35905         
35906         this.cmargins = c.cmargins || this.cmargins ||
35907                          (this.position == "west" || this.position == "east" ?
35908                              {top: 0, left: 2, right:2, bottom: 0} :
35909                              {top: 2, left: 0, right:0, bottom: 2});
35910         */
35911         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35912         
35913         
35914         this.bottomTabs = c.tabPosition != "top";
35915         
35916         this.autoScroll = c.autoScroll || false;
35917         
35918         
35919        
35920         
35921         this.duration = c.duration || .30;
35922         this.slideDuration = c.slideDuration || .45;
35923         this.config = c;
35924        
35925     },
35926     /**
35927      * Returns true if this region is currently visible.
35928      * @return {Boolean}
35929      */
35930     isVisible : function(){
35931         return this.visible;
35932     },
35933
35934     /**
35935      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35936      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35937      */
35938     //setCollapsedTitle : function(title){
35939     //    title = title || "&#160;";
35940      //   if(this.collapsedTitleTextEl){
35941       //      this.collapsedTitleTextEl.innerHTML = title;
35942        // }
35943     //},
35944
35945     getBox : function(){
35946         var b;
35947       //  if(!this.collapsed){
35948             b = this.el.getBox(false, true);
35949        // }else{
35950           //  b = this.collapsedEl.getBox(false, true);
35951         //}
35952         return b;
35953     },
35954
35955     getMargins : function(){
35956         return this.margins;
35957         //return this.collapsed ? this.cmargins : this.margins;
35958     },
35959 /*
35960     highlight : function(){
35961         this.el.addClass("x-layout-panel-dragover");
35962     },
35963
35964     unhighlight : function(){
35965         this.el.removeClass("x-layout-panel-dragover");
35966     },
35967 */
35968     updateBox : function(box)
35969     {
35970         if (!this.bodyEl) {
35971             return; // not rendered yet..
35972         }
35973         
35974         this.box = box;
35975         if(!this.collapsed){
35976             this.el.dom.style.left = box.x + "px";
35977             this.el.dom.style.top = box.y + "px";
35978             this.updateBody(box.width, box.height);
35979         }else{
35980             this.collapsedEl.dom.style.left = box.x + "px";
35981             this.collapsedEl.dom.style.top = box.y + "px";
35982             this.collapsedEl.setSize(box.width, box.height);
35983         }
35984         if(this.tabs){
35985             this.tabs.autoSizeTabs();
35986         }
35987     },
35988
35989     updateBody : function(w, h)
35990     {
35991         if(w !== null){
35992             this.el.setWidth(w);
35993             w -= this.el.getBorderWidth("rl");
35994             if(this.config.adjustments){
35995                 w += this.config.adjustments[0];
35996             }
35997         }
35998         if(h !== null && h > 0){
35999             this.el.setHeight(h);
36000             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36001             h -= this.el.getBorderWidth("tb");
36002             if(this.config.adjustments){
36003                 h += this.config.adjustments[1];
36004             }
36005             this.bodyEl.setHeight(h);
36006             if(this.tabs){
36007                 h = this.tabs.syncHeight(h);
36008             }
36009         }
36010         if(this.panelSize){
36011             w = w !== null ? w : this.panelSize.width;
36012             h = h !== null ? h : this.panelSize.height;
36013         }
36014         if(this.activePanel){
36015             var el = this.activePanel.getEl();
36016             w = w !== null ? w : el.getWidth();
36017             h = h !== null ? h : el.getHeight();
36018             this.panelSize = {width: w, height: h};
36019             this.activePanel.setSize(w, h);
36020         }
36021         if(Roo.isIE && this.tabs){
36022             this.tabs.el.repaint();
36023         }
36024     },
36025
36026     /**
36027      * Returns the container element for this region.
36028      * @return {Roo.Element}
36029      */
36030     getEl : function(){
36031         return this.el;
36032     },
36033
36034     /**
36035      * Hides this region.
36036      */
36037     hide : function(){
36038         //if(!this.collapsed){
36039             this.el.dom.style.left = "-2000px";
36040             this.el.hide();
36041         //}else{
36042          //   this.collapsedEl.dom.style.left = "-2000px";
36043          //   this.collapsedEl.hide();
36044        // }
36045         this.visible = false;
36046         this.fireEvent("visibilitychange", this, false);
36047     },
36048
36049     /**
36050      * Shows this region if it was previously hidden.
36051      */
36052     show : function(){
36053         //if(!this.collapsed){
36054             this.el.show();
36055         //}else{
36056         //    this.collapsedEl.show();
36057        // }
36058         this.visible = true;
36059         this.fireEvent("visibilitychange", this, true);
36060     },
36061 /*
36062     closeClicked : function(){
36063         if(this.activePanel){
36064             this.remove(this.activePanel);
36065         }
36066     },
36067
36068     collapseClick : function(e){
36069         if(this.isSlid){
36070            e.stopPropagation();
36071            this.slideIn();
36072         }else{
36073            e.stopPropagation();
36074            this.slideOut();
36075         }
36076     },
36077 */
36078     /**
36079      * Collapses this region.
36080      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36081      */
36082     /*
36083     collapse : function(skipAnim, skipCheck = false){
36084         if(this.collapsed) {
36085             return;
36086         }
36087         
36088         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36089             
36090             this.collapsed = true;
36091             if(this.split){
36092                 this.split.el.hide();
36093             }
36094             if(this.config.animate && skipAnim !== true){
36095                 this.fireEvent("invalidated", this);
36096                 this.animateCollapse();
36097             }else{
36098                 this.el.setLocation(-20000,-20000);
36099                 this.el.hide();
36100                 this.collapsedEl.show();
36101                 this.fireEvent("collapsed", this);
36102                 this.fireEvent("invalidated", this);
36103             }
36104         }
36105         
36106     },
36107 */
36108     animateCollapse : function(){
36109         // overridden
36110     },
36111
36112     /**
36113      * Expands this region if it was previously collapsed.
36114      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36115      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36116      */
36117     /*
36118     expand : function(e, skipAnim){
36119         if(e) {
36120             e.stopPropagation();
36121         }
36122         if(!this.collapsed || this.el.hasActiveFx()) {
36123             return;
36124         }
36125         if(this.isSlid){
36126             this.afterSlideIn();
36127             skipAnim = true;
36128         }
36129         this.collapsed = false;
36130         if(this.config.animate && skipAnim !== true){
36131             this.animateExpand();
36132         }else{
36133             this.el.show();
36134             if(this.split){
36135                 this.split.el.show();
36136             }
36137             this.collapsedEl.setLocation(-2000,-2000);
36138             this.collapsedEl.hide();
36139             this.fireEvent("invalidated", this);
36140             this.fireEvent("expanded", this);
36141         }
36142     },
36143 */
36144     animateExpand : function(){
36145         // overridden
36146     },
36147
36148     initTabs : function()
36149     {
36150         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36151         
36152         var ts = new Roo.bootstrap.panel.Tabs({
36153                 el: this.bodyEl.dom,
36154                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36155                 disableTooltips: this.config.disableTabTips,
36156                 toolbar : this.config.toolbar
36157             });
36158         
36159         if(this.config.hideTabs){
36160             ts.stripWrap.setDisplayed(false);
36161         }
36162         this.tabs = ts;
36163         ts.resizeTabs = this.config.resizeTabs === true;
36164         ts.minTabWidth = this.config.minTabWidth || 40;
36165         ts.maxTabWidth = this.config.maxTabWidth || 250;
36166         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36167         ts.monitorResize = false;
36168         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36169         ts.bodyEl.addClass('roo-layout-tabs-body');
36170         this.panels.each(this.initPanelAsTab, this);
36171     },
36172
36173     initPanelAsTab : function(panel){
36174         var ti = this.tabs.addTab(
36175             panel.getEl().id,
36176             panel.getTitle(),
36177             null,
36178             this.config.closeOnTab && panel.isClosable(),
36179             panel.tpl
36180         );
36181         if(panel.tabTip !== undefined){
36182             ti.setTooltip(panel.tabTip);
36183         }
36184         ti.on("activate", function(){
36185               this.setActivePanel(panel);
36186         }, this);
36187         
36188         if(this.config.closeOnTab){
36189             ti.on("beforeclose", function(t, e){
36190                 e.cancel = true;
36191                 this.remove(panel);
36192             }, this);
36193         }
36194         
36195         panel.tabItem = ti;
36196         
36197         return ti;
36198     },
36199
36200     updatePanelTitle : function(panel, title)
36201     {
36202         if(this.activePanel == panel){
36203             this.updateTitle(title);
36204         }
36205         if(this.tabs){
36206             var ti = this.tabs.getTab(panel.getEl().id);
36207             ti.setText(title);
36208             if(panel.tabTip !== undefined){
36209                 ti.setTooltip(panel.tabTip);
36210             }
36211         }
36212     },
36213
36214     updateTitle : function(title){
36215         if(this.titleTextEl && !this.config.title){
36216             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36217         }
36218     },
36219
36220     setActivePanel : function(panel)
36221     {
36222         panel = this.getPanel(panel);
36223         if(this.activePanel && this.activePanel != panel){
36224             if(this.activePanel.setActiveState(false) === false){
36225                 return;
36226             }
36227         }
36228         this.activePanel = panel;
36229         panel.setActiveState(true);
36230         if(this.panelSize){
36231             panel.setSize(this.panelSize.width, this.panelSize.height);
36232         }
36233         if(this.closeBtn){
36234             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36235         }
36236         this.updateTitle(panel.getTitle());
36237         if(this.tabs){
36238             this.fireEvent("invalidated", this);
36239         }
36240         this.fireEvent("panelactivated", this, panel);
36241     },
36242
36243     /**
36244      * Shows the specified panel.
36245      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36246      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36247      */
36248     showPanel : function(panel)
36249     {
36250         panel = this.getPanel(panel);
36251         if(panel){
36252             if(this.tabs){
36253                 var tab = this.tabs.getTab(panel.getEl().id);
36254                 if(tab.isHidden()){
36255                     this.tabs.unhideTab(tab.id);
36256                 }
36257                 tab.activate();
36258             }else{
36259                 this.setActivePanel(panel);
36260             }
36261         }
36262         return panel;
36263     },
36264
36265     /**
36266      * Get the active panel for this region.
36267      * @return {Roo.ContentPanel} The active panel or null
36268      */
36269     getActivePanel : function(){
36270         return this.activePanel;
36271     },
36272
36273     validateVisibility : function(){
36274         if(this.panels.getCount() < 1){
36275             this.updateTitle("&#160;");
36276             this.closeBtn.hide();
36277             this.hide();
36278         }else{
36279             if(!this.isVisible()){
36280                 this.show();
36281             }
36282         }
36283     },
36284
36285     /**
36286      * Adds the passed ContentPanel(s) to this region.
36287      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36288      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36289      */
36290     add : function(panel)
36291     {
36292         if(arguments.length > 1){
36293             for(var i = 0, len = arguments.length; i < len; i++) {
36294                 this.add(arguments[i]);
36295             }
36296             return null;
36297         }
36298         
36299         // if we have not been rendered yet, then we can not really do much of this..
36300         if (!this.bodyEl) {
36301             this.unrendered_panels.push(panel);
36302             return panel;
36303         }
36304         
36305         
36306         
36307         
36308         if(this.hasPanel(panel)){
36309             this.showPanel(panel);
36310             return panel;
36311         }
36312         panel.setRegion(this);
36313         this.panels.add(panel);
36314        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36315             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36316             // and hide them... ???
36317             this.bodyEl.dom.appendChild(panel.getEl().dom);
36318             if(panel.background !== true){
36319                 this.setActivePanel(panel);
36320             }
36321             this.fireEvent("paneladded", this, panel);
36322             return panel;
36323         }
36324         */
36325         if(!this.tabs){
36326             this.initTabs();
36327         }else{
36328             this.initPanelAsTab(panel);
36329         }
36330         
36331         
36332         if(panel.background !== true){
36333             this.tabs.activate(panel.getEl().id);
36334         }
36335         this.fireEvent("paneladded", this, panel);
36336         return panel;
36337     },
36338
36339     /**
36340      * Hides the tab for the specified panel.
36341      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36342      */
36343     hidePanel : function(panel){
36344         if(this.tabs && (panel = this.getPanel(panel))){
36345             this.tabs.hideTab(panel.getEl().id);
36346         }
36347     },
36348
36349     /**
36350      * Unhides the tab for a previously hidden panel.
36351      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36352      */
36353     unhidePanel : function(panel){
36354         if(this.tabs && (panel = this.getPanel(panel))){
36355             this.tabs.unhideTab(panel.getEl().id);
36356         }
36357     },
36358
36359     clearPanels : function(){
36360         while(this.panels.getCount() > 0){
36361              this.remove(this.panels.first());
36362         }
36363     },
36364
36365     /**
36366      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36367      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36368      * @param {Boolean} preservePanel Overrides the config preservePanel option
36369      * @return {Roo.ContentPanel} The panel that was removed
36370      */
36371     remove : function(panel, preservePanel)
36372     {
36373         panel = this.getPanel(panel);
36374         if(!panel){
36375             return null;
36376         }
36377         var e = {};
36378         this.fireEvent("beforeremove", this, panel, e);
36379         if(e.cancel === true){
36380             return null;
36381         }
36382         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36383         var panelId = panel.getId();
36384         this.panels.removeKey(panelId);
36385         if(preservePanel){
36386             document.body.appendChild(panel.getEl().dom);
36387         }
36388         if(this.tabs){
36389             this.tabs.removeTab(panel.getEl().id);
36390         }else if (!preservePanel){
36391             this.bodyEl.dom.removeChild(panel.getEl().dom);
36392         }
36393         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36394             var p = this.panels.first();
36395             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36396             tempEl.appendChild(p.getEl().dom);
36397             this.bodyEl.update("");
36398             this.bodyEl.dom.appendChild(p.getEl().dom);
36399             tempEl = null;
36400             this.updateTitle(p.getTitle());
36401             this.tabs = null;
36402             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36403             this.setActivePanel(p);
36404         }
36405         panel.setRegion(null);
36406         if(this.activePanel == panel){
36407             this.activePanel = null;
36408         }
36409         if(this.config.autoDestroy !== false && preservePanel !== true){
36410             try{panel.destroy();}catch(e){}
36411         }
36412         this.fireEvent("panelremoved", this, panel);
36413         return panel;
36414     },
36415
36416     /**
36417      * Returns the TabPanel component used by this region
36418      * @return {Roo.TabPanel}
36419      */
36420     getTabs : function(){
36421         return this.tabs;
36422     },
36423
36424     createTool : function(parentEl, className){
36425         var btn = Roo.DomHelper.append(parentEl, {
36426             tag: "div",
36427             cls: "x-layout-tools-button",
36428             children: [ {
36429                 tag: "div",
36430                 cls: "roo-layout-tools-button-inner " + className,
36431                 html: "&#160;"
36432             }]
36433         }, true);
36434         btn.addClassOnOver("roo-layout-tools-button-over");
36435         return btn;
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447  
36448
36449
36450 /**
36451  * @class Roo.SplitLayoutRegion
36452  * @extends Roo.LayoutRegion
36453  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36454  */
36455 Roo.bootstrap.layout.Split = function(config){
36456     this.cursor = config.cursor;
36457     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36458 };
36459
36460 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36461 {
36462     splitTip : "Drag to resize.",
36463     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36464     useSplitTips : false,
36465
36466     applyConfig : function(config){
36467         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36468     },
36469     
36470     onRender : function(ctr,pos) {
36471         
36472         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36473         if(!this.config.split){
36474             return;
36475         }
36476         if(!this.split){
36477             
36478             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36479                             tag: "div",
36480                             id: this.el.id + "-split",
36481                             cls: "roo-layout-split roo-layout-split-"+this.position,
36482                             html: "&#160;"
36483             });
36484             /** The SplitBar for this region 
36485             * @type Roo.SplitBar */
36486             // does not exist yet...
36487             Roo.log([this.position, this.orientation]);
36488             
36489             this.split = new Roo.bootstrap.SplitBar({
36490                 dragElement : splitEl,
36491                 resizingElement: this.el,
36492                 orientation : this.orientation
36493             });
36494             
36495             this.split.on("moved", this.onSplitMove, this);
36496             this.split.useShim = this.config.useShim === true;
36497             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36498             if(this.useSplitTips){
36499                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36500             }
36501             //if(config.collapsible){
36502             //    this.split.el.on("dblclick", this.collapse,  this);
36503             //}
36504         }
36505         if(typeof this.config.minSize != "undefined"){
36506             this.split.minSize = this.config.minSize;
36507         }
36508         if(typeof this.config.maxSize != "undefined"){
36509             this.split.maxSize = this.config.maxSize;
36510         }
36511         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36512             this.hideSplitter();
36513         }
36514         
36515     },
36516
36517     getHMaxSize : function(){
36518          var cmax = this.config.maxSize || 10000;
36519          var center = this.mgr.getRegion("center");
36520          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36521     },
36522
36523     getVMaxSize : function(){
36524          var cmax = this.config.maxSize || 10000;
36525          var center = this.mgr.getRegion("center");
36526          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36527     },
36528
36529     onSplitMove : function(split, newSize){
36530         this.fireEvent("resized", this, newSize);
36531     },
36532     
36533     /** 
36534      * Returns the {@link Roo.SplitBar} for this region.
36535      * @return {Roo.SplitBar}
36536      */
36537     getSplitBar : function(){
36538         return this.split;
36539     },
36540     
36541     hide : function(){
36542         this.hideSplitter();
36543         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36544     },
36545
36546     hideSplitter : function(){
36547         if(this.split){
36548             this.split.el.setLocation(-2000,-2000);
36549             this.split.el.hide();
36550         }
36551     },
36552
36553     show : function(){
36554         if(this.split){
36555             this.split.el.show();
36556         }
36557         Roo.bootstrap.layout.Split.superclass.show.call(this);
36558     },
36559     
36560     beforeSlide: function(){
36561         if(Roo.isGecko){// firefox overflow auto bug workaround
36562             this.bodyEl.clip();
36563             if(this.tabs) {
36564                 this.tabs.bodyEl.clip();
36565             }
36566             if(this.activePanel){
36567                 this.activePanel.getEl().clip();
36568                 
36569                 if(this.activePanel.beforeSlide){
36570                     this.activePanel.beforeSlide();
36571                 }
36572             }
36573         }
36574     },
36575     
36576     afterSlide : function(){
36577         if(Roo.isGecko){// firefox overflow auto bug workaround
36578             this.bodyEl.unclip();
36579             if(this.tabs) {
36580                 this.tabs.bodyEl.unclip();
36581             }
36582             if(this.activePanel){
36583                 this.activePanel.getEl().unclip();
36584                 if(this.activePanel.afterSlide){
36585                     this.activePanel.afterSlide();
36586                 }
36587             }
36588         }
36589     },
36590
36591     initAutoHide : function(){
36592         if(this.autoHide !== false){
36593             if(!this.autoHideHd){
36594                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36595                 this.autoHideHd = {
36596                     "mouseout": function(e){
36597                         if(!e.within(this.el, true)){
36598                             st.delay(500);
36599                         }
36600                     },
36601                     "mouseover" : function(e){
36602                         st.cancel();
36603                     },
36604                     scope : this
36605                 };
36606             }
36607             this.el.on(this.autoHideHd);
36608         }
36609     },
36610
36611     clearAutoHide : function(){
36612         if(this.autoHide !== false){
36613             this.el.un("mouseout", this.autoHideHd.mouseout);
36614             this.el.un("mouseover", this.autoHideHd.mouseover);
36615         }
36616     },
36617
36618     clearMonitor : function(){
36619         Roo.get(document).un("click", this.slideInIf, this);
36620     },
36621
36622     // these names are backwards but not changed for compat
36623     slideOut : function(){
36624         if(this.isSlid || this.el.hasActiveFx()){
36625             return;
36626         }
36627         this.isSlid = true;
36628         if(this.collapseBtn){
36629             this.collapseBtn.hide();
36630         }
36631         this.closeBtnState = this.closeBtn.getStyle('display');
36632         this.closeBtn.hide();
36633         if(this.stickBtn){
36634             this.stickBtn.show();
36635         }
36636         this.el.show();
36637         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36638         this.beforeSlide();
36639         this.el.setStyle("z-index", 10001);
36640         this.el.slideIn(this.getSlideAnchor(), {
36641             callback: function(){
36642                 this.afterSlide();
36643                 this.initAutoHide();
36644                 Roo.get(document).on("click", this.slideInIf, this);
36645                 this.fireEvent("slideshow", this);
36646             },
36647             scope: this,
36648             block: true
36649         });
36650     },
36651
36652     afterSlideIn : function(){
36653         this.clearAutoHide();
36654         this.isSlid = false;
36655         this.clearMonitor();
36656         this.el.setStyle("z-index", "");
36657         if(this.collapseBtn){
36658             this.collapseBtn.show();
36659         }
36660         this.closeBtn.setStyle('display', this.closeBtnState);
36661         if(this.stickBtn){
36662             this.stickBtn.hide();
36663         }
36664         this.fireEvent("slidehide", this);
36665     },
36666
36667     slideIn : function(cb){
36668         if(!this.isSlid || this.el.hasActiveFx()){
36669             Roo.callback(cb);
36670             return;
36671         }
36672         this.isSlid = false;
36673         this.beforeSlide();
36674         this.el.slideOut(this.getSlideAnchor(), {
36675             callback: function(){
36676                 this.el.setLeftTop(-10000, -10000);
36677                 this.afterSlide();
36678                 this.afterSlideIn();
36679                 Roo.callback(cb);
36680             },
36681             scope: this,
36682             block: true
36683         });
36684     },
36685     
36686     slideInIf : function(e){
36687         if(!e.within(this.el)){
36688             this.slideIn();
36689         }
36690     },
36691
36692     animateCollapse : function(){
36693         this.beforeSlide();
36694         this.el.setStyle("z-index", 20000);
36695         var anchor = this.getSlideAnchor();
36696         this.el.slideOut(anchor, {
36697             callback : function(){
36698                 this.el.setStyle("z-index", "");
36699                 this.collapsedEl.slideIn(anchor, {duration:.3});
36700                 this.afterSlide();
36701                 this.el.setLocation(-10000,-10000);
36702                 this.el.hide();
36703                 this.fireEvent("collapsed", this);
36704             },
36705             scope: this,
36706             block: true
36707         });
36708     },
36709
36710     animateExpand : function(){
36711         this.beforeSlide();
36712         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36713         this.el.setStyle("z-index", 20000);
36714         this.collapsedEl.hide({
36715             duration:.1
36716         });
36717         this.el.slideIn(this.getSlideAnchor(), {
36718             callback : function(){
36719                 this.el.setStyle("z-index", "");
36720                 this.afterSlide();
36721                 if(this.split){
36722                     this.split.el.show();
36723                 }
36724                 this.fireEvent("invalidated", this);
36725                 this.fireEvent("expanded", this);
36726             },
36727             scope: this,
36728             block: true
36729         });
36730     },
36731
36732     anchors : {
36733         "west" : "left",
36734         "east" : "right",
36735         "north" : "top",
36736         "south" : "bottom"
36737     },
36738
36739     sanchors : {
36740         "west" : "l",
36741         "east" : "r",
36742         "north" : "t",
36743         "south" : "b"
36744     },
36745
36746     canchors : {
36747         "west" : "tl-tr",
36748         "east" : "tr-tl",
36749         "north" : "tl-bl",
36750         "south" : "bl-tl"
36751     },
36752
36753     getAnchor : function(){
36754         return this.anchors[this.position];
36755     },
36756
36757     getCollapseAnchor : function(){
36758         return this.canchors[this.position];
36759     },
36760
36761     getSlideAnchor : function(){
36762         return this.sanchors[this.position];
36763     },
36764
36765     getAlignAdj : function(){
36766         var cm = this.cmargins;
36767         switch(this.position){
36768             case "west":
36769                 return [0, 0];
36770             break;
36771             case "east":
36772                 return [0, 0];
36773             break;
36774             case "north":
36775                 return [0, 0];
36776             break;
36777             case "south":
36778                 return [0, 0];
36779             break;
36780         }
36781     },
36782
36783     getExpandAdj : function(){
36784         var c = this.collapsedEl, cm = this.cmargins;
36785         switch(this.position){
36786             case "west":
36787                 return [-(cm.right+c.getWidth()+cm.left), 0];
36788             break;
36789             case "east":
36790                 return [cm.right+c.getWidth()+cm.left, 0];
36791             break;
36792             case "north":
36793                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36794             break;
36795             case "south":
36796                 return [0, cm.top+cm.bottom+c.getHeight()];
36797             break;
36798         }
36799     }
36800 });/*
36801  * Based on:
36802  * Ext JS Library 1.1.1
36803  * Copyright(c) 2006-2007, Ext JS, LLC.
36804  *
36805  * Originally Released Under LGPL - original licence link has changed is not relivant.
36806  *
36807  * Fork - LGPL
36808  * <script type="text/javascript">
36809  */
36810 /*
36811  * These classes are private internal classes
36812  */
36813 Roo.bootstrap.layout.Center = function(config){
36814     config.region = "center";
36815     Roo.bootstrap.layout.Region.call(this, config);
36816     this.visible = true;
36817     this.minWidth = config.minWidth || 20;
36818     this.minHeight = config.minHeight || 20;
36819 };
36820
36821 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36822     hide : function(){
36823         // center panel can't be hidden
36824     },
36825     
36826     show : function(){
36827         // center panel can't be hidden
36828     },
36829     
36830     getMinWidth: function(){
36831         return this.minWidth;
36832     },
36833     
36834     getMinHeight: function(){
36835         return this.minHeight;
36836     }
36837 });
36838
36839
36840
36841
36842  
36843
36844
36845
36846
36847
36848 Roo.bootstrap.layout.North = function(config)
36849 {
36850     config.region = 'north';
36851     config.cursor = 'n-resize';
36852     
36853     Roo.bootstrap.layout.Split.call(this, config);
36854     
36855     
36856     if(this.split){
36857         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36858         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36859         this.split.el.addClass("roo-layout-split-v");
36860     }
36861     var size = config.initialSize || config.height;
36862     if(typeof size != "undefined"){
36863         this.el.setHeight(size);
36864     }
36865 };
36866 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36867 {
36868     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36869     
36870     
36871     
36872     getBox : function(){
36873         if(this.collapsed){
36874             return this.collapsedEl.getBox();
36875         }
36876         var box = this.el.getBox();
36877         if(this.split){
36878             box.height += this.split.el.getHeight();
36879         }
36880         return box;
36881     },
36882     
36883     updateBox : function(box){
36884         if(this.split && !this.collapsed){
36885             box.height -= this.split.el.getHeight();
36886             this.split.el.setLeft(box.x);
36887             this.split.el.setTop(box.y+box.height);
36888             this.split.el.setWidth(box.width);
36889         }
36890         if(this.collapsed){
36891             this.updateBody(box.width, null);
36892         }
36893         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36894     }
36895 });
36896
36897
36898
36899
36900
36901 Roo.bootstrap.layout.South = function(config){
36902     config.region = 'south';
36903     config.cursor = 's-resize';
36904     Roo.bootstrap.layout.Split.call(this, config);
36905     if(this.split){
36906         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36907         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36908         this.split.el.addClass("roo-layout-split-v");
36909     }
36910     var size = config.initialSize || config.height;
36911     if(typeof size != "undefined"){
36912         this.el.setHeight(size);
36913     }
36914 };
36915
36916 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36917     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36918     getBox : function(){
36919         if(this.collapsed){
36920             return this.collapsedEl.getBox();
36921         }
36922         var box = this.el.getBox();
36923         if(this.split){
36924             var sh = this.split.el.getHeight();
36925             box.height += sh;
36926             box.y -= sh;
36927         }
36928         return box;
36929     },
36930     
36931     updateBox : function(box){
36932         if(this.split && !this.collapsed){
36933             var sh = this.split.el.getHeight();
36934             box.height -= sh;
36935             box.y += sh;
36936             this.split.el.setLeft(box.x);
36937             this.split.el.setTop(box.y-sh);
36938             this.split.el.setWidth(box.width);
36939         }
36940         if(this.collapsed){
36941             this.updateBody(box.width, null);
36942         }
36943         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36944     }
36945 });
36946
36947 Roo.bootstrap.layout.East = function(config){
36948     config.region = "east";
36949     config.cursor = "e-resize";
36950     Roo.bootstrap.layout.Split.call(this, config);
36951     if(this.split){
36952         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36953         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36954         this.split.el.addClass("roo-layout-split-h");
36955     }
36956     var size = config.initialSize || config.width;
36957     if(typeof size != "undefined"){
36958         this.el.setWidth(size);
36959     }
36960 };
36961 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36962     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36963     getBox : function(){
36964         if(this.collapsed){
36965             return this.collapsedEl.getBox();
36966         }
36967         var box = this.el.getBox();
36968         if(this.split){
36969             var sw = this.split.el.getWidth();
36970             box.width += sw;
36971             box.x -= sw;
36972         }
36973         return box;
36974     },
36975
36976     updateBox : function(box){
36977         if(this.split && !this.collapsed){
36978             var sw = this.split.el.getWidth();
36979             box.width -= sw;
36980             this.split.el.setLeft(box.x);
36981             this.split.el.setTop(box.y);
36982             this.split.el.setHeight(box.height);
36983             box.x += sw;
36984         }
36985         if(this.collapsed){
36986             this.updateBody(null, box.height);
36987         }
36988         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36989     }
36990 });
36991
36992 Roo.bootstrap.layout.West = function(config){
36993     config.region = "west";
36994     config.cursor = "w-resize";
36995     
36996     Roo.bootstrap.layout.Split.call(this, config);
36997     if(this.split){
36998         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36999         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37000         this.split.el.addClass("roo-layout-split-h");
37001     }
37002     
37003 };
37004 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37005     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37006     
37007     onRender: function(ctr, pos)
37008     {
37009         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37010         var size = this.config.initialSize || this.config.width;
37011         if(typeof size != "undefined"){
37012             this.el.setWidth(size);
37013         }
37014     },
37015     
37016     getBox : function(){
37017         if(this.collapsed){
37018             return this.collapsedEl.getBox();
37019         }
37020         var box = this.el.getBox();
37021         if(this.split){
37022             box.width += this.split.el.getWidth();
37023         }
37024         return box;
37025     },
37026     
37027     updateBox : function(box){
37028         if(this.split && !this.collapsed){
37029             var sw = this.split.el.getWidth();
37030             box.width -= sw;
37031             this.split.el.setLeft(box.x+box.width);
37032             this.split.el.setTop(box.y);
37033             this.split.el.setHeight(box.height);
37034         }
37035         if(this.collapsed){
37036             this.updateBody(null, box.height);
37037         }
37038         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37039     }
37040 });
37041 Roo.namespace("Roo.bootstrap.panel");/*
37042  * Based on:
37043  * Ext JS Library 1.1.1
37044  * Copyright(c) 2006-2007, Ext JS, LLC.
37045  *
37046  * Originally Released Under LGPL - original licence link has changed is not relivant.
37047  *
37048  * Fork - LGPL
37049  * <script type="text/javascript">
37050  */
37051 /**
37052  * @class Roo.ContentPanel
37053  * @extends Roo.util.Observable
37054  * A basic ContentPanel element.
37055  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37056  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37057  * @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
37058  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37059  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37060  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37061  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37062  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37063  * @cfg {String} title          The title for this panel
37064  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37065  * @cfg {String} url            Calls {@link #setUrl} with this value
37066  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37067  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37068  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37069  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37070  * @cfg {Boolean} badges render the badges
37071
37072  * @constructor
37073  * Create a new ContentPanel.
37074  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37075  * @param {String/Object} config A string to set only the title or a config object
37076  * @param {String} content (optional) Set the HTML content for this panel
37077  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37078  */
37079 Roo.bootstrap.panel.Content = function( config){
37080     
37081     this.tpl = config.tpl || false;
37082     
37083     var el = config.el;
37084     var content = config.content;
37085
37086     if(config.autoCreate){ // xtype is available if this is called from factory
37087         el = Roo.id();
37088     }
37089     this.el = Roo.get(el);
37090     if(!this.el && config && config.autoCreate){
37091         if(typeof config.autoCreate == "object"){
37092             if(!config.autoCreate.id){
37093                 config.autoCreate.id = config.id||el;
37094             }
37095             this.el = Roo.DomHelper.append(document.body,
37096                         config.autoCreate, true);
37097         }else{
37098             var elcfg =  {   tag: "div",
37099                             cls: "roo-layout-inactive-content",
37100                             id: config.id||el
37101                             };
37102             if (config.html) {
37103                 elcfg.html = config.html;
37104                 
37105             }
37106                         
37107             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37108         }
37109     } 
37110     this.closable = false;
37111     this.loaded = false;
37112     this.active = false;
37113    
37114       
37115     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37116         
37117         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37118         
37119         this.wrapEl = this.el; //this.el.wrap();
37120         var ti = [];
37121         if (config.toolbar.items) {
37122             ti = config.toolbar.items ;
37123             delete config.toolbar.items ;
37124         }
37125         
37126         var nitems = [];
37127         this.toolbar.render(this.wrapEl, 'before');
37128         for(var i =0;i < ti.length;i++) {
37129           //  Roo.log(['add child', items[i]]);
37130             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37131         }
37132         this.toolbar.items = nitems;
37133         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37134         delete config.toolbar;
37135         
37136     }
37137     /*
37138     // xtype created footer. - not sure if will work as we normally have to render first..
37139     if (this.footer && !this.footer.el && this.footer.xtype) {
37140         if (!this.wrapEl) {
37141             this.wrapEl = this.el.wrap();
37142         }
37143     
37144         this.footer.container = this.wrapEl.createChild();
37145          
37146         this.footer = Roo.factory(this.footer, Roo);
37147         
37148     }
37149     */
37150     
37151      if(typeof config == "string"){
37152         this.title = config;
37153     }else{
37154         Roo.apply(this, config);
37155     }
37156     
37157     if(this.resizeEl){
37158         this.resizeEl = Roo.get(this.resizeEl, true);
37159     }else{
37160         this.resizeEl = this.el;
37161     }
37162     // handle view.xtype
37163     
37164  
37165     
37166     
37167     this.addEvents({
37168         /**
37169          * @event activate
37170          * Fires when this panel is activated. 
37171          * @param {Roo.ContentPanel} this
37172          */
37173         "activate" : true,
37174         /**
37175          * @event deactivate
37176          * Fires when this panel is activated. 
37177          * @param {Roo.ContentPanel} this
37178          */
37179         "deactivate" : true,
37180
37181         /**
37182          * @event resize
37183          * Fires when this panel is resized if fitToFrame is true.
37184          * @param {Roo.ContentPanel} this
37185          * @param {Number} width The width after any component adjustments
37186          * @param {Number} height The height after any component adjustments
37187          */
37188         "resize" : true,
37189         
37190          /**
37191          * @event render
37192          * Fires when this tab is created
37193          * @param {Roo.ContentPanel} this
37194          */
37195         "render" : true
37196         
37197         
37198         
37199     });
37200     
37201
37202     
37203     
37204     if(this.autoScroll){
37205         this.resizeEl.setStyle("overflow", "auto");
37206     } else {
37207         // fix randome scrolling
37208         //this.el.on('scroll', function() {
37209         //    Roo.log('fix random scolling');
37210         //    this.scrollTo('top',0); 
37211         //});
37212     }
37213     content = content || this.content;
37214     if(content){
37215         this.setContent(content);
37216     }
37217     if(config && config.url){
37218         this.setUrl(this.url, this.params, this.loadOnce);
37219     }
37220     
37221     
37222     
37223     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37224     
37225     if (this.view && typeof(this.view.xtype) != 'undefined') {
37226         this.view.el = this.el.appendChild(document.createElement("div"));
37227         this.view = Roo.factory(this.view); 
37228         this.view.render  &&  this.view.render(false, '');  
37229     }
37230     
37231     
37232     this.fireEvent('render', this);
37233 };
37234
37235 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37236     
37237     tabTip : '',
37238     
37239     setRegion : function(region){
37240         this.region = region;
37241         this.setActiveClass(region && !this.background);
37242     },
37243     
37244     
37245     setActiveClass: function(state)
37246     {
37247         if(state){
37248            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37249            this.el.setStyle('position','relative');
37250         }else{
37251            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37252            this.el.setStyle('position', 'absolute');
37253         } 
37254     },
37255     
37256     /**
37257      * Returns the toolbar for this Panel if one was configured. 
37258      * @return {Roo.Toolbar} 
37259      */
37260     getToolbar : function(){
37261         return this.toolbar;
37262     },
37263     
37264     setActiveState : function(active)
37265     {
37266         this.active = active;
37267         this.setActiveClass(active);
37268         if(!active){
37269             if(this.fireEvent("deactivate", this) === false){
37270                 return false;
37271             }
37272             return true;
37273         }
37274         this.fireEvent("activate", this);
37275         return true;
37276     },
37277     /**
37278      * Updates this panel's element
37279      * @param {String} content The new content
37280      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37281     */
37282     setContent : function(content, loadScripts){
37283         this.el.update(content, loadScripts);
37284     },
37285
37286     ignoreResize : function(w, h){
37287         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37288             return true;
37289         }else{
37290             this.lastSize = {width: w, height: h};
37291             return false;
37292         }
37293     },
37294     /**
37295      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37296      * @return {Roo.UpdateManager} The UpdateManager
37297      */
37298     getUpdateManager : function(){
37299         return this.el.getUpdateManager();
37300     },
37301      /**
37302      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37303      * @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:
37304 <pre><code>
37305 panel.load({
37306     url: "your-url.php",
37307     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37308     callback: yourFunction,
37309     scope: yourObject, //(optional scope)
37310     discardUrl: false,
37311     nocache: false,
37312     text: "Loading...",
37313     timeout: 30,
37314     scripts: false
37315 });
37316 </code></pre>
37317      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37318      * 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.
37319      * @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}
37320      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37321      * @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.
37322      * @return {Roo.ContentPanel} this
37323      */
37324     load : function(){
37325         var um = this.el.getUpdateManager();
37326         um.update.apply(um, arguments);
37327         return this;
37328     },
37329
37330
37331     /**
37332      * 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.
37333      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37334      * @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)
37335      * @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)
37336      * @return {Roo.UpdateManager} The UpdateManager
37337      */
37338     setUrl : function(url, params, loadOnce){
37339         if(this.refreshDelegate){
37340             this.removeListener("activate", this.refreshDelegate);
37341         }
37342         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37343         this.on("activate", this.refreshDelegate);
37344         return this.el.getUpdateManager();
37345     },
37346     
37347     _handleRefresh : function(url, params, loadOnce){
37348         if(!loadOnce || !this.loaded){
37349             var updater = this.el.getUpdateManager();
37350             updater.update(url, params, this._setLoaded.createDelegate(this));
37351         }
37352     },
37353     
37354     _setLoaded : function(){
37355         this.loaded = true;
37356     }, 
37357     
37358     /**
37359      * Returns this panel's id
37360      * @return {String} 
37361      */
37362     getId : function(){
37363         return this.el.id;
37364     },
37365     
37366     /** 
37367      * Returns this panel's element - used by regiosn to add.
37368      * @return {Roo.Element} 
37369      */
37370     getEl : function(){
37371         return this.wrapEl || this.el;
37372     },
37373     
37374    
37375     
37376     adjustForComponents : function(width, height)
37377     {
37378         //Roo.log('adjustForComponents ');
37379         if(this.resizeEl != this.el){
37380             width -= this.el.getFrameWidth('lr');
37381             height -= this.el.getFrameWidth('tb');
37382         }
37383         if(this.toolbar){
37384             var te = this.toolbar.getEl();
37385             te.setWidth(width);
37386             height -= te.getHeight();
37387         }
37388         if(this.footer){
37389             var te = this.footer.getEl();
37390             te.setWidth(width);
37391             height -= te.getHeight();
37392         }
37393         
37394         
37395         if(this.adjustments){
37396             width += this.adjustments[0];
37397             height += this.adjustments[1];
37398         }
37399         return {"width": width, "height": height};
37400     },
37401     
37402     setSize : function(width, height){
37403         if(this.fitToFrame && !this.ignoreResize(width, height)){
37404             if(this.fitContainer && this.resizeEl != this.el){
37405                 this.el.setSize(width, height);
37406             }
37407             var size = this.adjustForComponents(width, height);
37408             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37409             this.fireEvent('resize', this, size.width, size.height);
37410         }
37411     },
37412     
37413     /**
37414      * Returns this panel's title
37415      * @return {String} 
37416      */
37417     getTitle : function(){
37418         
37419         if (typeof(this.title) != 'object') {
37420             return this.title;
37421         }
37422         
37423         var t = '';
37424         for (var k in this.title) {
37425             if (!this.title.hasOwnProperty(k)) {
37426                 continue;
37427             }
37428             
37429             if (k.indexOf('-') >= 0) {
37430                 var s = k.split('-');
37431                 for (var i = 0; i<s.length; i++) {
37432                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37433                 }
37434             } else {
37435                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37436             }
37437         }
37438         return t;
37439     },
37440     
37441     /**
37442      * Set this panel's title
37443      * @param {String} title
37444      */
37445     setTitle : function(title){
37446         this.title = title;
37447         if(this.region){
37448             this.region.updatePanelTitle(this, title);
37449         }
37450     },
37451     
37452     /**
37453      * Returns true is this panel was configured to be closable
37454      * @return {Boolean} 
37455      */
37456     isClosable : function(){
37457         return this.closable;
37458     },
37459     
37460     beforeSlide : function(){
37461         this.el.clip();
37462         this.resizeEl.clip();
37463     },
37464     
37465     afterSlide : function(){
37466         this.el.unclip();
37467         this.resizeEl.unclip();
37468     },
37469     
37470     /**
37471      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37472      *   Will fail silently if the {@link #setUrl} method has not been called.
37473      *   This does not activate the panel, just updates its content.
37474      */
37475     refresh : function(){
37476         if(this.refreshDelegate){
37477            this.loaded = false;
37478            this.refreshDelegate();
37479         }
37480     },
37481     
37482     /**
37483      * Destroys this panel
37484      */
37485     destroy : function(){
37486         this.el.removeAllListeners();
37487         var tempEl = document.createElement("span");
37488         tempEl.appendChild(this.el.dom);
37489         tempEl.innerHTML = "";
37490         this.el.remove();
37491         this.el = null;
37492     },
37493     
37494     /**
37495      * form - if the content panel contains a form - this is a reference to it.
37496      * @type {Roo.form.Form}
37497      */
37498     form : false,
37499     /**
37500      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37501      *    This contains a reference to it.
37502      * @type {Roo.View}
37503      */
37504     view : false,
37505     
37506       /**
37507      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37508      * <pre><code>
37509
37510 layout.addxtype({
37511        xtype : 'Form',
37512        items: [ .... ]
37513    }
37514 );
37515
37516 </code></pre>
37517      * @param {Object} cfg Xtype definition of item to add.
37518      */
37519     
37520     
37521     getChildContainer: function () {
37522         return this.getEl();
37523     }
37524     
37525     
37526     /*
37527         var  ret = new Roo.factory(cfg);
37528         return ret;
37529         
37530         
37531         // add form..
37532         if (cfg.xtype.match(/^Form$/)) {
37533             
37534             var el;
37535             //if (this.footer) {
37536             //    el = this.footer.container.insertSibling(false, 'before');
37537             //} else {
37538                 el = this.el.createChild();
37539             //}
37540
37541             this.form = new  Roo.form.Form(cfg);
37542             
37543             
37544             if ( this.form.allItems.length) {
37545                 this.form.render(el.dom);
37546             }
37547             return this.form;
37548         }
37549         // should only have one of theses..
37550         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37551             // views.. should not be just added - used named prop 'view''
37552             
37553             cfg.el = this.el.appendChild(document.createElement("div"));
37554             // factory?
37555             
37556             var ret = new Roo.factory(cfg);
37557              
37558              ret.render && ret.render(false, ''); // render blank..
37559             this.view = ret;
37560             return ret;
37561         }
37562         return false;
37563     }
37564     \*/
37565 });
37566  
37567 /**
37568  * @class Roo.bootstrap.panel.Grid
37569  * @extends Roo.bootstrap.panel.Content
37570  * @constructor
37571  * Create a new GridPanel.
37572  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37573  * @param {Object} config A the config object
37574   
37575  */
37576
37577
37578
37579 Roo.bootstrap.panel.Grid = function(config)
37580 {
37581     
37582       
37583     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37584         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37585
37586     config.el = this.wrapper;
37587     //this.el = this.wrapper;
37588     
37589       if (config.container) {
37590         // ctor'ed from a Border/panel.grid
37591         
37592         
37593         this.wrapper.setStyle("overflow", "hidden");
37594         this.wrapper.addClass('roo-grid-container');
37595
37596     }
37597     
37598     
37599     if(config.toolbar){
37600         var tool_el = this.wrapper.createChild();    
37601         this.toolbar = Roo.factory(config.toolbar);
37602         var ti = [];
37603         if (config.toolbar.items) {
37604             ti = config.toolbar.items ;
37605             delete config.toolbar.items ;
37606         }
37607         
37608         var nitems = [];
37609         this.toolbar.render(tool_el);
37610         for(var i =0;i < ti.length;i++) {
37611           //  Roo.log(['add child', items[i]]);
37612             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37613         }
37614         this.toolbar.items = nitems;
37615         
37616         delete config.toolbar;
37617     }
37618     
37619     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37620     config.grid.scrollBody = true;;
37621     config.grid.monitorWindowResize = false; // turn off autosizing
37622     config.grid.autoHeight = false;
37623     config.grid.autoWidth = false;
37624     
37625     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37626     
37627     if (config.background) {
37628         // render grid on panel activation (if panel background)
37629         this.on('activate', function(gp) {
37630             if (!gp.grid.rendered) {
37631                 gp.grid.render(this.wrapper);
37632                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37633             }
37634         });
37635             
37636     } else {
37637         this.grid.render(this.wrapper);
37638         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37639
37640     }
37641     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37642     // ??? needed ??? config.el = this.wrapper;
37643     
37644     
37645     
37646   
37647     // xtype created footer. - not sure if will work as we normally have to render first..
37648     if (this.footer && !this.footer.el && this.footer.xtype) {
37649         
37650         var ctr = this.grid.getView().getFooterPanel(true);
37651         this.footer.dataSource = this.grid.dataSource;
37652         this.footer = Roo.factory(this.footer, Roo);
37653         this.footer.render(ctr);
37654         
37655     }
37656     
37657     
37658     
37659     
37660      
37661 };
37662
37663 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37664     getId : function(){
37665         return this.grid.id;
37666     },
37667     
37668     /**
37669      * Returns the grid for this panel
37670      * @return {Roo.bootstrap.Table} 
37671      */
37672     getGrid : function(){
37673         return this.grid;    
37674     },
37675     
37676     setSize : function(width, height){
37677         if(!this.ignoreResize(width, height)){
37678             var grid = this.grid;
37679             var size = this.adjustForComponents(width, height);
37680             var gridel = grid.getGridEl();
37681             gridel.setSize(size.width, size.height);
37682             /*
37683             var thd = grid.getGridEl().select('thead',true).first();
37684             var tbd = grid.getGridEl().select('tbody', true).first();
37685             if (tbd) {
37686                 tbd.setSize(width, height - thd.getHeight());
37687             }
37688             */
37689             grid.autoSize();
37690         }
37691     },
37692      
37693     
37694     
37695     beforeSlide : function(){
37696         this.grid.getView().scroller.clip();
37697     },
37698     
37699     afterSlide : function(){
37700         this.grid.getView().scroller.unclip();
37701     },
37702     
37703     destroy : function(){
37704         this.grid.destroy();
37705         delete this.grid;
37706         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37707     }
37708 });
37709
37710 /**
37711  * @class Roo.bootstrap.panel.Nest
37712  * @extends Roo.bootstrap.panel.Content
37713  * @constructor
37714  * Create a new Panel, that can contain a layout.Border.
37715  * 
37716  * 
37717  * @param {Roo.BorderLayout} layout The layout for this panel
37718  * @param {String/Object} config A string to set only the title or a config object
37719  */
37720 Roo.bootstrap.panel.Nest = function(config)
37721 {
37722     // construct with only one argument..
37723     /* FIXME - implement nicer consturctors
37724     if (layout.layout) {
37725         config = layout;
37726         layout = config.layout;
37727         delete config.layout;
37728     }
37729     if (layout.xtype && !layout.getEl) {
37730         // then layout needs constructing..
37731         layout = Roo.factory(layout, Roo);
37732     }
37733     */
37734     
37735     config.el =  config.layout.getEl();
37736     
37737     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37738     
37739     config.layout.monitorWindowResize = false; // turn off autosizing
37740     this.layout = config.layout;
37741     this.layout.getEl().addClass("roo-layout-nested-layout");
37742     
37743     
37744     
37745     
37746 };
37747
37748 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37749
37750     setSize : function(width, height){
37751         if(!this.ignoreResize(width, height)){
37752             var size = this.adjustForComponents(width, height);
37753             var el = this.layout.getEl();
37754             if (size.height < 1) {
37755                 el.setWidth(size.width);   
37756             } else {
37757                 el.setSize(size.width, size.height);
37758             }
37759             var touch = el.dom.offsetWidth;
37760             this.layout.layout();
37761             // ie requires a double layout on the first pass
37762             if(Roo.isIE && !this.initialized){
37763                 this.initialized = true;
37764                 this.layout.layout();
37765             }
37766         }
37767     },
37768     
37769     // activate all subpanels if not currently active..
37770     
37771     setActiveState : function(active){
37772         this.active = active;
37773         this.setActiveClass(active);
37774         
37775         if(!active){
37776             this.fireEvent("deactivate", this);
37777             return;
37778         }
37779         
37780         this.fireEvent("activate", this);
37781         // not sure if this should happen before or after..
37782         if (!this.layout) {
37783             return; // should not happen..
37784         }
37785         var reg = false;
37786         for (var r in this.layout.regions) {
37787             reg = this.layout.getRegion(r);
37788             if (reg.getActivePanel()) {
37789                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37790                 reg.setActivePanel(reg.getActivePanel());
37791                 continue;
37792             }
37793             if (!reg.panels.length) {
37794                 continue;
37795             }
37796             reg.showPanel(reg.getPanel(0));
37797         }
37798         
37799         
37800         
37801         
37802     },
37803     
37804     /**
37805      * Returns the nested BorderLayout for this panel
37806      * @return {Roo.BorderLayout} 
37807      */
37808     getLayout : function(){
37809         return this.layout;
37810     },
37811     
37812      /**
37813      * Adds a xtype elements to the layout of the nested panel
37814      * <pre><code>
37815
37816 panel.addxtype({
37817        xtype : 'ContentPanel',
37818        region: 'west',
37819        items: [ .... ]
37820    }
37821 );
37822
37823 panel.addxtype({
37824         xtype : 'NestedLayoutPanel',
37825         region: 'west',
37826         layout: {
37827            center: { },
37828            west: { }   
37829         },
37830         items : [ ... list of content panels or nested layout panels.. ]
37831    }
37832 );
37833 </code></pre>
37834      * @param {Object} cfg Xtype definition of item to add.
37835      */
37836     addxtype : function(cfg) {
37837         return this.layout.addxtype(cfg);
37838     
37839     }
37840 });        /*
37841  * Based on:
37842  * Ext JS Library 1.1.1
37843  * Copyright(c) 2006-2007, Ext JS, LLC.
37844  *
37845  * Originally Released Under LGPL - original licence link has changed is not relivant.
37846  *
37847  * Fork - LGPL
37848  * <script type="text/javascript">
37849  */
37850 /**
37851  * @class Roo.TabPanel
37852  * @extends Roo.util.Observable
37853  * A lightweight tab container.
37854  * <br><br>
37855  * Usage:
37856  * <pre><code>
37857 // basic tabs 1, built from existing content
37858 var tabs = new Roo.TabPanel("tabs1");
37859 tabs.addTab("script", "View Script");
37860 tabs.addTab("markup", "View Markup");
37861 tabs.activate("script");
37862
37863 // more advanced tabs, built from javascript
37864 var jtabs = new Roo.TabPanel("jtabs");
37865 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37866
37867 // set up the UpdateManager
37868 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37869 var updater = tab2.getUpdateManager();
37870 updater.setDefaultUrl("ajax1.htm");
37871 tab2.on('activate', updater.refresh, updater, true);
37872
37873 // Use setUrl for Ajax loading
37874 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37875 tab3.setUrl("ajax2.htm", null, true);
37876
37877 // Disabled tab
37878 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37879 tab4.disable();
37880
37881 jtabs.activate("jtabs-1");
37882  * </code></pre>
37883  * @constructor
37884  * Create a new TabPanel.
37885  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37886  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37887  */
37888 Roo.bootstrap.panel.Tabs = function(config){
37889     /**
37890     * The container element for this TabPanel.
37891     * @type Roo.Element
37892     */
37893     this.el = Roo.get(config.el);
37894     delete config.el;
37895     if(config){
37896         if(typeof config == "boolean"){
37897             this.tabPosition = config ? "bottom" : "top";
37898         }else{
37899             Roo.apply(this, config);
37900         }
37901     }
37902     
37903     if(this.tabPosition == "bottom"){
37904         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37905         this.el.addClass("roo-tabs-bottom");
37906     }
37907     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37908     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37909     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37910     if(Roo.isIE){
37911         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37912     }
37913     if(this.tabPosition != "bottom"){
37914         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37915          * @type Roo.Element
37916          */
37917         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37918         this.el.addClass("roo-tabs-top");
37919     }
37920     this.items = [];
37921
37922     this.bodyEl.setStyle("position", "relative");
37923
37924     this.active = null;
37925     this.activateDelegate = this.activate.createDelegate(this);
37926
37927     this.addEvents({
37928         /**
37929          * @event tabchange
37930          * Fires when the active tab changes
37931          * @param {Roo.TabPanel} this
37932          * @param {Roo.TabPanelItem} activePanel The new active tab
37933          */
37934         "tabchange": true,
37935         /**
37936          * @event beforetabchange
37937          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37938          * @param {Roo.TabPanel} this
37939          * @param {Object} e Set cancel to true on this object to cancel the tab change
37940          * @param {Roo.TabPanelItem} tab The tab being changed to
37941          */
37942         "beforetabchange" : true
37943     });
37944
37945     Roo.EventManager.onWindowResize(this.onResize, this);
37946     this.cpad = this.el.getPadding("lr");
37947     this.hiddenCount = 0;
37948
37949
37950     // toolbar on the tabbar support...
37951     if (this.toolbar) {
37952         alert("no toolbar support yet");
37953         this.toolbar  = false;
37954         /*
37955         var tcfg = this.toolbar;
37956         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37957         this.toolbar = new Roo.Toolbar(tcfg);
37958         if (Roo.isSafari) {
37959             var tbl = tcfg.container.child('table', true);
37960             tbl.setAttribute('width', '100%');
37961         }
37962         */
37963         
37964     }
37965    
37966
37967
37968     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37969 };
37970
37971 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37972     /*
37973      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37974      */
37975     tabPosition : "top",
37976     /*
37977      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37978      */
37979     currentTabWidth : 0,
37980     /*
37981      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37982      */
37983     minTabWidth : 40,
37984     /*
37985      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37986      */
37987     maxTabWidth : 250,
37988     /*
37989      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37990      */
37991     preferredTabWidth : 175,
37992     /*
37993      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37994      */
37995     resizeTabs : false,
37996     /*
37997      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37998      */
37999     monitorResize : true,
38000     /*
38001      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38002      */
38003     toolbar : false,
38004
38005     /**
38006      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38007      * @param {String} id The id of the div to use <b>or create</b>
38008      * @param {String} text The text for the tab
38009      * @param {String} content (optional) Content to put in the TabPanelItem body
38010      * @param {Boolean} closable (optional) True to create a close icon on the tab
38011      * @return {Roo.TabPanelItem} The created TabPanelItem
38012      */
38013     addTab : function(id, text, content, closable, tpl)
38014     {
38015         var item = new Roo.bootstrap.panel.TabItem({
38016             panel: this,
38017             id : id,
38018             text : text,
38019             closable : closable,
38020             tpl : tpl
38021         });
38022         this.addTabItem(item);
38023         if(content){
38024             item.setContent(content);
38025         }
38026         return item;
38027     },
38028
38029     /**
38030      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38031      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38032      * @return {Roo.TabPanelItem}
38033      */
38034     getTab : function(id){
38035         return this.items[id];
38036     },
38037
38038     /**
38039      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38040      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38041      */
38042     hideTab : function(id){
38043         var t = this.items[id];
38044         if(!t.isHidden()){
38045            t.setHidden(true);
38046            this.hiddenCount++;
38047            this.autoSizeTabs();
38048         }
38049     },
38050
38051     /**
38052      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38053      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38054      */
38055     unhideTab : function(id){
38056         var t = this.items[id];
38057         if(t.isHidden()){
38058            t.setHidden(false);
38059            this.hiddenCount--;
38060            this.autoSizeTabs();
38061         }
38062     },
38063
38064     /**
38065      * Adds an existing {@link Roo.TabPanelItem}.
38066      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38067      */
38068     addTabItem : function(item){
38069         this.items[item.id] = item;
38070         this.items.push(item);
38071       //  if(this.resizeTabs){
38072     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38073   //         this.autoSizeTabs();
38074 //        }else{
38075 //            item.autoSize();
38076        // }
38077     },
38078
38079     /**
38080      * Removes a {@link Roo.TabPanelItem}.
38081      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38082      */
38083     removeTab : function(id){
38084         var items = this.items;
38085         var tab = items[id];
38086         if(!tab) { return; }
38087         var index = items.indexOf(tab);
38088         if(this.active == tab && items.length > 1){
38089             var newTab = this.getNextAvailable(index);
38090             if(newTab) {
38091                 newTab.activate();
38092             }
38093         }
38094         this.stripEl.dom.removeChild(tab.pnode.dom);
38095         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38096             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38097         }
38098         items.splice(index, 1);
38099         delete this.items[tab.id];
38100         tab.fireEvent("close", tab);
38101         tab.purgeListeners();
38102         this.autoSizeTabs();
38103     },
38104
38105     getNextAvailable : function(start){
38106         var items = this.items;
38107         var index = start;
38108         // look for a next tab that will slide over to
38109         // replace the one being removed
38110         while(index < items.length){
38111             var item = items[++index];
38112             if(item && !item.isHidden()){
38113                 return item;
38114             }
38115         }
38116         // if one isn't found select the previous tab (on the left)
38117         index = start;
38118         while(index >= 0){
38119             var item = items[--index];
38120             if(item && !item.isHidden()){
38121                 return item;
38122             }
38123         }
38124         return null;
38125     },
38126
38127     /**
38128      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38129      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38130      */
38131     disableTab : function(id){
38132         var tab = this.items[id];
38133         if(tab && this.active != tab){
38134             tab.disable();
38135         }
38136     },
38137
38138     /**
38139      * Enables a {@link Roo.TabPanelItem} that is disabled.
38140      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38141      */
38142     enableTab : function(id){
38143         var tab = this.items[id];
38144         tab.enable();
38145     },
38146
38147     /**
38148      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38149      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38150      * @return {Roo.TabPanelItem} The TabPanelItem.
38151      */
38152     activate : function(id){
38153         var tab = this.items[id];
38154         if(!tab){
38155             return null;
38156         }
38157         if(tab == this.active || tab.disabled){
38158             return tab;
38159         }
38160         var e = {};
38161         this.fireEvent("beforetabchange", this, e, tab);
38162         if(e.cancel !== true && !tab.disabled){
38163             if(this.active){
38164                 this.active.hide();
38165             }
38166             this.active = this.items[id];
38167             this.active.show();
38168             this.fireEvent("tabchange", this, this.active);
38169         }
38170         return tab;
38171     },
38172
38173     /**
38174      * Gets the active {@link Roo.TabPanelItem}.
38175      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38176      */
38177     getActiveTab : function(){
38178         return this.active;
38179     },
38180
38181     /**
38182      * Updates the tab body element to fit the height of the container element
38183      * for overflow scrolling
38184      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38185      */
38186     syncHeight : function(targetHeight){
38187         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38188         var bm = this.bodyEl.getMargins();
38189         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38190         this.bodyEl.setHeight(newHeight);
38191         return newHeight;
38192     },
38193
38194     onResize : function(){
38195         if(this.monitorResize){
38196             this.autoSizeTabs();
38197         }
38198     },
38199
38200     /**
38201      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38202      */
38203     beginUpdate : function(){
38204         this.updating = true;
38205     },
38206
38207     /**
38208      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38209      */
38210     endUpdate : function(){
38211         this.updating = false;
38212         this.autoSizeTabs();
38213     },
38214
38215     /**
38216      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38217      */
38218     autoSizeTabs : function(){
38219         var count = this.items.length;
38220         var vcount = count - this.hiddenCount;
38221         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38222             return;
38223         }
38224         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38225         var availWidth = Math.floor(w / vcount);
38226         var b = this.stripBody;
38227         if(b.getWidth() > w){
38228             var tabs = this.items;
38229             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38230             if(availWidth < this.minTabWidth){
38231                 /*if(!this.sleft){    // incomplete scrolling code
38232                     this.createScrollButtons();
38233                 }
38234                 this.showScroll();
38235                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38236             }
38237         }else{
38238             if(this.currentTabWidth < this.preferredTabWidth){
38239                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38240             }
38241         }
38242     },
38243
38244     /**
38245      * Returns the number of tabs in this TabPanel.
38246      * @return {Number}
38247      */
38248      getCount : function(){
38249          return this.items.length;
38250      },
38251
38252     /**
38253      * Resizes all the tabs to the passed width
38254      * @param {Number} The new width
38255      */
38256     setTabWidth : function(width){
38257         this.currentTabWidth = width;
38258         for(var i = 0, len = this.items.length; i < len; i++) {
38259                 if(!this.items[i].isHidden()) {
38260                 this.items[i].setWidth(width);
38261             }
38262         }
38263     },
38264
38265     /**
38266      * Destroys this TabPanel
38267      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38268      */
38269     destroy : function(removeEl){
38270         Roo.EventManager.removeResizeListener(this.onResize, this);
38271         for(var i = 0, len = this.items.length; i < len; i++){
38272             this.items[i].purgeListeners();
38273         }
38274         if(removeEl === true){
38275             this.el.update("");
38276             this.el.remove();
38277         }
38278     },
38279     
38280     createStrip : function(container)
38281     {
38282         var strip = document.createElement("nav");
38283         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38284         container.appendChild(strip);
38285         return strip;
38286     },
38287     
38288     createStripList : function(strip)
38289     {
38290         // div wrapper for retard IE
38291         // returns the "tr" element.
38292         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38293         //'<div class="x-tabs-strip-wrap">'+
38294           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38295           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38296         return strip.firstChild; //.firstChild.firstChild.firstChild;
38297     },
38298     createBody : function(container)
38299     {
38300         var body = document.createElement("div");
38301         Roo.id(body, "tab-body");
38302         //Roo.fly(body).addClass("x-tabs-body");
38303         Roo.fly(body).addClass("tab-content");
38304         container.appendChild(body);
38305         return body;
38306     },
38307     createItemBody :function(bodyEl, id){
38308         var body = Roo.getDom(id);
38309         if(!body){
38310             body = document.createElement("div");
38311             body.id = id;
38312         }
38313         //Roo.fly(body).addClass("x-tabs-item-body");
38314         Roo.fly(body).addClass("tab-pane");
38315          bodyEl.insertBefore(body, bodyEl.firstChild);
38316         return body;
38317     },
38318     /** @private */
38319     createStripElements :  function(stripEl, text, closable, tpl)
38320     {
38321         var td = document.createElement("li"); // was td..
38322         
38323         
38324         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38325         
38326         
38327         stripEl.appendChild(td);
38328         /*if(closable){
38329             td.className = "x-tabs-closable";
38330             if(!this.closeTpl){
38331                 this.closeTpl = new Roo.Template(
38332                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38333                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38334                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38335                 );
38336             }
38337             var el = this.closeTpl.overwrite(td, {"text": text});
38338             var close = el.getElementsByTagName("div")[0];
38339             var inner = el.getElementsByTagName("em")[0];
38340             return {"el": el, "close": close, "inner": inner};
38341         } else {
38342         */
38343         // not sure what this is..
38344 //            if(!this.tabTpl){
38345                 //this.tabTpl = new Roo.Template(
38346                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38347                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38348                 //);
38349 //                this.tabTpl = new Roo.Template(
38350 //                   '<a href="#">' +
38351 //                   '<span unselectable="on"' +
38352 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38353 //                            ' >{text}</span></a>'
38354 //                );
38355 //                
38356 //            }
38357
38358
38359             var template = tpl || this.tabTpl || false;
38360             
38361             if(!template){
38362                 
38363                 template = new Roo.Template(
38364                    '<a href="#">' +
38365                    '<span unselectable="on"' +
38366                             (this.disableTooltips ? '' : ' title="{text}"') +
38367                             ' >{text}</span></a>'
38368                 );
38369             }
38370             
38371             switch (typeof(template)) {
38372                 case 'object' :
38373                     break;
38374                 case 'string' :
38375                     template = new Roo.Template(template);
38376                     break;
38377                 default :
38378                     break;
38379             }
38380             
38381             var el = template.overwrite(td, {"text": text});
38382             
38383             var inner = el.getElementsByTagName("span")[0];
38384             
38385             return {"el": el, "inner": inner};
38386             
38387     }
38388         
38389     
38390 });
38391
38392 /**
38393  * @class Roo.TabPanelItem
38394  * @extends Roo.util.Observable
38395  * Represents an individual item (tab plus body) in a TabPanel.
38396  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38397  * @param {String} id The id of this TabPanelItem
38398  * @param {String} text The text for the tab of this TabPanelItem
38399  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38400  */
38401 Roo.bootstrap.panel.TabItem = function(config){
38402     /**
38403      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38404      * @type Roo.TabPanel
38405      */
38406     this.tabPanel = config.panel;
38407     /**
38408      * The id for this TabPanelItem
38409      * @type String
38410      */
38411     this.id = config.id;
38412     /** @private */
38413     this.disabled = false;
38414     /** @private */
38415     this.text = config.text;
38416     /** @private */
38417     this.loaded = false;
38418     this.closable = config.closable;
38419
38420     /**
38421      * The body element for this TabPanelItem.
38422      * @type Roo.Element
38423      */
38424     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38425     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38426     this.bodyEl.setStyle("display", "block");
38427     this.bodyEl.setStyle("zoom", "1");
38428     //this.hideAction();
38429
38430     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38431     /** @private */
38432     this.el = Roo.get(els.el);
38433     this.inner = Roo.get(els.inner, true);
38434     this.textEl = Roo.get(this.el.dom.firstChild, true);
38435     this.pnode = Roo.get(els.el.parentNode, true);
38436 //    this.el.on("mousedown", this.onTabMouseDown, this);
38437     this.el.on("click", this.onTabClick, this);
38438     /** @private */
38439     if(config.closable){
38440         var c = Roo.get(els.close, true);
38441         c.dom.title = this.closeText;
38442         c.addClassOnOver("close-over");
38443         c.on("click", this.closeClick, this);
38444      }
38445
38446     this.addEvents({
38447          /**
38448          * @event activate
38449          * Fires when this tab becomes the active tab.
38450          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38451          * @param {Roo.TabPanelItem} this
38452          */
38453         "activate": true,
38454         /**
38455          * @event beforeclose
38456          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38457          * @param {Roo.TabPanelItem} this
38458          * @param {Object} e Set cancel to true on this object to cancel the close.
38459          */
38460         "beforeclose": true,
38461         /**
38462          * @event close
38463          * Fires when this tab is closed.
38464          * @param {Roo.TabPanelItem} this
38465          */
38466          "close": true,
38467         /**
38468          * @event deactivate
38469          * Fires when this tab is no longer the active tab.
38470          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38471          * @param {Roo.TabPanelItem} this
38472          */
38473          "deactivate" : true
38474     });
38475     this.hidden = false;
38476
38477     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38478 };
38479
38480 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38481            {
38482     purgeListeners : function(){
38483        Roo.util.Observable.prototype.purgeListeners.call(this);
38484        this.el.removeAllListeners();
38485     },
38486     /**
38487      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38488      */
38489     show : function(){
38490         this.pnode.addClass("active");
38491         this.showAction();
38492         if(Roo.isOpera){
38493             this.tabPanel.stripWrap.repaint();
38494         }
38495         this.fireEvent("activate", this.tabPanel, this);
38496     },
38497
38498     /**
38499      * Returns true if this tab is the active tab.
38500      * @return {Boolean}
38501      */
38502     isActive : function(){
38503         return this.tabPanel.getActiveTab() == this;
38504     },
38505
38506     /**
38507      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38508      */
38509     hide : function(){
38510         this.pnode.removeClass("active");
38511         this.hideAction();
38512         this.fireEvent("deactivate", this.tabPanel, this);
38513     },
38514
38515     hideAction : function(){
38516         this.bodyEl.hide();
38517         this.bodyEl.setStyle("position", "absolute");
38518         this.bodyEl.setLeft("-20000px");
38519         this.bodyEl.setTop("-20000px");
38520     },
38521
38522     showAction : function(){
38523         this.bodyEl.setStyle("position", "relative");
38524         this.bodyEl.setTop("");
38525         this.bodyEl.setLeft("");
38526         this.bodyEl.show();
38527     },
38528
38529     /**
38530      * Set the tooltip for the tab.
38531      * @param {String} tooltip The tab's tooltip
38532      */
38533     setTooltip : function(text){
38534         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38535             this.textEl.dom.qtip = text;
38536             this.textEl.dom.removeAttribute('title');
38537         }else{
38538             this.textEl.dom.title = text;
38539         }
38540     },
38541
38542     onTabClick : function(e){
38543         e.preventDefault();
38544         this.tabPanel.activate(this.id);
38545     },
38546
38547     onTabMouseDown : function(e){
38548         e.preventDefault();
38549         this.tabPanel.activate(this.id);
38550     },
38551 /*
38552     getWidth : function(){
38553         return this.inner.getWidth();
38554     },
38555
38556     setWidth : function(width){
38557         var iwidth = width - this.pnode.getPadding("lr");
38558         this.inner.setWidth(iwidth);
38559         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38560         this.pnode.setWidth(width);
38561     },
38562 */
38563     /**
38564      * Show or hide the tab
38565      * @param {Boolean} hidden True to hide or false to show.
38566      */
38567     setHidden : function(hidden){
38568         this.hidden = hidden;
38569         this.pnode.setStyle("display", hidden ? "none" : "");
38570     },
38571
38572     /**
38573      * Returns true if this tab is "hidden"
38574      * @return {Boolean}
38575      */
38576     isHidden : function(){
38577         return this.hidden;
38578     },
38579
38580     /**
38581      * Returns the text for this tab
38582      * @return {String}
38583      */
38584     getText : function(){
38585         return this.text;
38586     },
38587     /*
38588     autoSize : function(){
38589         //this.el.beginMeasure();
38590         this.textEl.setWidth(1);
38591         /*
38592          *  #2804 [new] Tabs in Roojs
38593          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38594          */
38595         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38596         //this.el.endMeasure();
38597     //},
38598
38599     /**
38600      * Sets the text for the tab (Note: this also sets the tooltip text)
38601      * @param {String} text The tab's text and tooltip
38602      */
38603     setText : function(text){
38604         this.text = text;
38605         this.textEl.update(text);
38606         this.setTooltip(text);
38607         //if(!this.tabPanel.resizeTabs){
38608         //    this.autoSize();
38609         //}
38610     },
38611     /**
38612      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38613      */
38614     activate : function(){
38615         this.tabPanel.activate(this.id);
38616     },
38617
38618     /**
38619      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38620      */
38621     disable : function(){
38622         if(this.tabPanel.active != this){
38623             this.disabled = true;
38624             this.pnode.addClass("disabled");
38625         }
38626     },
38627
38628     /**
38629      * Enables this TabPanelItem if it was previously disabled.
38630      */
38631     enable : function(){
38632         this.disabled = false;
38633         this.pnode.removeClass("disabled");
38634     },
38635
38636     /**
38637      * Sets the content for this TabPanelItem.
38638      * @param {String} content The content
38639      * @param {Boolean} loadScripts true to look for and load scripts
38640      */
38641     setContent : function(content, loadScripts){
38642         this.bodyEl.update(content, loadScripts);
38643     },
38644
38645     /**
38646      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38647      * @return {Roo.UpdateManager} The UpdateManager
38648      */
38649     getUpdateManager : function(){
38650         return this.bodyEl.getUpdateManager();
38651     },
38652
38653     /**
38654      * Set a URL to be used to load the content for this TabPanelItem.
38655      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38656      * @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)
38657      * @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)
38658      * @return {Roo.UpdateManager} The UpdateManager
38659      */
38660     setUrl : function(url, params, loadOnce){
38661         if(this.refreshDelegate){
38662             this.un('activate', this.refreshDelegate);
38663         }
38664         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38665         this.on("activate", this.refreshDelegate);
38666         return this.bodyEl.getUpdateManager();
38667     },
38668
38669     /** @private */
38670     _handleRefresh : function(url, params, loadOnce){
38671         if(!loadOnce || !this.loaded){
38672             var updater = this.bodyEl.getUpdateManager();
38673             updater.update(url, params, this._setLoaded.createDelegate(this));
38674         }
38675     },
38676
38677     /**
38678      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38679      *   Will fail silently if the setUrl method has not been called.
38680      *   This does not activate the panel, just updates its content.
38681      */
38682     refresh : function(){
38683         if(this.refreshDelegate){
38684            this.loaded = false;
38685            this.refreshDelegate();
38686         }
38687     },
38688
38689     /** @private */
38690     _setLoaded : function(){
38691         this.loaded = true;
38692     },
38693
38694     /** @private */
38695     closeClick : function(e){
38696         var o = {};
38697         e.stopEvent();
38698         this.fireEvent("beforeclose", this, o);
38699         if(o.cancel !== true){
38700             this.tabPanel.removeTab(this.id);
38701         }
38702     },
38703     /**
38704      * The text displayed in the tooltip for the close icon.
38705      * @type String
38706      */
38707     closeText : "Close this tab"
38708 });
38709 /**
38710 *    This script refer to:
38711 *    Title: International Telephone Input
38712 *    Author: Jack O'Connor
38713 *    Code version:  v12.1.12
38714 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38715 **/
38716
38717 Roo.bootstrap.PhoneInputData = function() {
38718     var d = [
38719       [
38720         "Afghanistan (‫افغانستان‬‎)",
38721         "af",
38722         "93"
38723       ],
38724       [
38725         "Albania (Shqipëri)",
38726         "al",
38727         "355"
38728       ],
38729       [
38730         "Algeria (‫الجزائر‬‎)",
38731         "dz",
38732         "213"
38733       ],
38734       [
38735         "American Samoa",
38736         "as",
38737         "1684"
38738       ],
38739       [
38740         "Andorra",
38741         "ad",
38742         "376"
38743       ],
38744       [
38745         "Angola",
38746         "ao",
38747         "244"
38748       ],
38749       [
38750         "Anguilla",
38751         "ai",
38752         "1264"
38753       ],
38754       [
38755         "Antigua and Barbuda",
38756         "ag",
38757         "1268"
38758       ],
38759       [
38760         "Argentina",
38761         "ar",
38762         "54"
38763       ],
38764       [
38765         "Armenia (Հայաստան)",
38766         "am",
38767         "374"
38768       ],
38769       [
38770         "Aruba",
38771         "aw",
38772         "297"
38773       ],
38774       [
38775         "Australia",
38776         "au",
38777         "61",
38778         0
38779       ],
38780       [
38781         "Austria (Österreich)",
38782         "at",
38783         "43"
38784       ],
38785       [
38786         "Azerbaijan (Azərbaycan)",
38787         "az",
38788         "994"
38789       ],
38790       [
38791         "Bahamas",
38792         "bs",
38793         "1242"
38794       ],
38795       [
38796         "Bahrain (‫البحرين‬‎)",
38797         "bh",
38798         "973"
38799       ],
38800       [
38801         "Bangladesh (বাংলাদেশ)",
38802         "bd",
38803         "880"
38804       ],
38805       [
38806         "Barbados",
38807         "bb",
38808         "1246"
38809       ],
38810       [
38811         "Belarus (Беларусь)",
38812         "by",
38813         "375"
38814       ],
38815       [
38816         "Belgium (België)",
38817         "be",
38818         "32"
38819       ],
38820       [
38821         "Belize",
38822         "bz",
38823         "501"
38824       ],
38825       [
38826         "Benin (Bénin)",
38827         "bj",
38828         "229"
38829       ],
38830       [
38831         "Bermuda",
38832         "bm",
38833         "1441"
38834       ],
38835       [
38836         "Bhutan (འབྲུག)",
38837         "bt",
38838         "975"
38839       ],
38840       [
38841         "Bolivia",
38842         "bo",
38843         "591"
38844       ],
38845       [
38846         "Bosnia and Herzegovina (Босна и Херцеговина)",
38847         "ba",
38848         "387"
38849       ],
38850       [
38851         "Botswana",
38852         "bw",
38853         "267"
38854       ],
38855       [
38856         "Brazil (Brasil)",
38857         "br",
38858         "55"
38859       ],
38860       [
38861         "British Indian Ocean Territory",
38862         "io",
38863         "246"
38864       ],
38865       [
38866         "British Virgin Islands",
38867         "vg",
38868         "1284"
38869       ],
38870       [
38871         "Brunei",
38872         "bn",
38873         "673"
38874       ],
38875       [
38876         "Bulgaria (България)",
38877         "bg",
38878         "359"
38879       ],
38880       [
38881         "Burkina Faso",
38882         "bf",
38883         "226"
38884       ],
38885       [
38886         "Burundi (Uburundi)",
38887         "bi",
38888         "257"
38889       ],
38890       [
38891         "Cambodia (កម្ពុជា)",
38892         "kh",
38893         "855"
38894       ],
38895       [
38896         "Cameroon (Cameroun)",
38897         "cm",
38898         "237"
38899       ],
38900       [
38901         "Canada",
38902         "ca",
38903         "1",
38904         1,
38905         ["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"]
38906       ],
38907       [
38908         "Cape Verde (Kabu Verdi)",
38909         "cv",
38910         "238"
38911       ],
38912       [
38913         "Caribbean Netherlands",
38914         "bq",
38915         "599",
38916         1
38917       ],
38918       [
38919         "Cayman Islands",
38920         "ky",
38921         "1345"
38922       ],
38923       [
38924         "Central African Republic (République centrafricaine)",
38925         "cf",
38926         "236"
38927       ],
38928       [
38929         "Chad (Tchad)",
38930         "td",
38931         "235"
38932       ],
38933       [
38934         "Chile",
38935         "cl",
38936         "56"
38937       ],
38938       [
38939         "China (中国)",
38940         "cn",
38941         "86"
38942       ],
38943       [
38944         "Christmas Island",
38945         "cx",
38946         "61",
38947         2
38948       ],
38949       [
38950         "Cocos (Keeling) Islands",
38951         "cc",
38952         "61",
38953         1
38954       ],
38955       [
38956         "Colombia",
38957         "co",
38958         "57"
38959       ],
38960       [
38961         "Comoros (‫جزر القمر‬‎)",
38962         "km",
38963         "269"
38964       ],
38965       [
38966         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38967         "cd",
38968         "243"
38969       ],
38970       [
38971         "Congo (Republic) (Congo-Brazzaville)",
38972         "cg",
38973         "242"
38974       ],
38975       [
38976         "Cook Islands",
38977         "ck",
38978         "682"
38979       ],
38980       [
38981         "Costa Rica",
38982         "cr",
38983         "506"
38984       ],
38985       [
38986         "Côte d’Ivoire",
38987         "ci",
38988         "225"
38989       ],
38990       [
38991         "Croatia (Hrvatska)",
38992         "hr",
38993         "385"
38994       ],
38995       [
38996         "Cuba",
38997         "cu",
38998         "53"
38999       ],
39000       [
39001         "Curaçao",
39002         "cw",
39003         "599",
39004         0
39005       ],
39006       [
39007         "Cyprus (Κύπρος)",
39008         "cy",
39009         "357"
39010       ],
39011       [
39012         "Czech Republic (Česká republika)",
39013         "cz",
39014         "420"
39015       ],
39016       [
39017         "Denmark (Danmark)",
39018         "dk",
39019         "45"
39020       ],
39021       [
39022         "Djibouti",
39023         "dj",
39024         "253"
39025       ],
39026       [
39027         "Dominica",
39028         "dm",
39029         "1767"
39030       ],
39031       [
39032         "Dominican Republic (República Dominicana)",
39033         "do",
39034         "1",
39035         2,
39036         ["809", "829", "849"]
39037       ],
39038       [
39039         "Ecuador",
39040         "ec",
39041         "593"
39042       ],
39043       [
39044         "Egypt (‫مصر‬‎)",
39045         "eg",
39046         "20"
39047       ],
39048       [
39049         "El Salvador",
39050         "sv",
39051         "503"
39052       ],
39053       [
39054         "Equatorial Guinea (Guinea Ecuatorial)",
39055         "gq",
39056         "240"
39057       ],
39058       [
39059         "Eritrea",
39060         "er",
39061         "291"
39062       ],
39063       [
39064         "Estonia (Eesti)",
39065         "ee",
39066         "372"
39067       ],
39068       [
39069         "Ethiopia",
39070         "et",
39071         "251"
39072       ],
39073       [
39074         "Falkland Islands (Islas Malvinas)",
39075         "fk",
39076         "500"
39077       ],
39078       [
39079         "Faroe Islands (Føroyar)",
39080         "fo",
39081         "298"
39082       ],
39083       [
39084         "Fiji",
39085         "fj",
39086         "679"
39087       ],
39088       [
39089         "Finland (Suomi)",
39090         "fi",
39091         "358",
39092         0
39093       ],
39094       [
39095         "France",
39096         "fr",
39097         "33"
39098       ],
39099       [
39100         "French Guiana (Guyane française)",
39101         "gf",
39102         "594"
39103       ],
39104       [
39105         "French Polynesia (Polynésie française)",
39106         "pf",
39107         "689"
39108       ],
39109       [
39110         "Gabon",
39111         "ga",
39112         "241"
39113       ],
39114       [
39115         "Gambia",
39116         "gm",
39117         "220"
39118       ],
39119       [
39120         "Georgia (საქართველო)",
39121         "ge",
39122         "995"
39123       ],
39124       [
39125         "Germany (Deutschland)",
39126         "de",
39127         "49"
39128       ],
39129       [
39130         "Ghana (Gaana)",
39131         "gh",
39132         "233"
39133       ],
39134       [
39135         "Gibraltar",
39136         "gi",
39137         "350"
39138       ],
39139       [
39140         "Greece (Ελλάδα)",
39141         "gr",
39142         "30"
39143       ],
39144       [
39145         "Greenland (Kalaallit Nunaat)",
39146         "gl",
39147         "299"
39148       ],
39149       [
39150         "Grenada",
39151         "gd",
39152         "1473"
39153       ],
39154       [
39155         "Guadeloupe",
39156         "gp",
39157         "590",
39158         0
39159       ],
39160       [
39161         "Guam",
39162         "gu",
39163         "1671"
39164       ],
39165       [
39166         "Guatemala",
39167         "gt",
39168         "502"
39169       ],
39170       [
39171         "Guernsey",
39172         "gg",
39173         "44",
39174         1
39175       ],
39176       [
39177         "Guinea (Guinée)",
39178         "gn",
39179         "224"
39180       ],
39181       [
39182         "Guinea-Bissau (Guiné Bissau)",
39183         "gw",
39184         "245"
39185       ],
39186       [
39187         "Guyana",
39188         "gy",
39189         "592"
39190       ],
39191       [
39192         "Haiti",
39193         "ht",
39194         "509"
39195       ],
39196       [
39197         "Honduras",
39198         "hn",
39199         "504"
39200       ],
39201       [
39202         "Hong Kong (香港)",
39203         "hk",
39204         "852"
39205       ],
39206       [
39207         "Hungary (Magyarország)",
39208         "hu",
39209         "36"
39210       ],
39211       [
39212         "Iceland (Ísland)",
39213         "is",
39214         "354"
39215       ],
39216       [
39217         "India (भारत)",
39218         "in",
39219         "91"
39220       ],
39221       [
39222         "Indonesia",
39223         "id",
39224         "62"
39225       ],
39226       [
39227         "Iran (‫ایران‬‎)",
39228         "ir",
39229         "98"
39230       ],
39231       [
39232         "Iraq (‫العراق‬‎)",
39233         "iq",
39234         "964"
39235       ],
39236       [
39237         "Ireland",
39238         "ie",
39239         "353"
39240       ],
39241       [
39242         "Isle of Man",
39243         "im",
39244         "44",
39245         2
39246       ],
39247       [
39248         "Israel (‫ישראל‬‎)",
39249         "il",
39250         "972"
39251       ],
39252       [
39253         "Italy (Italia)",
39254         "it",
39255         "39",
39256         0
39257       ],
39258       [
39259         "Jamaica",
39260         "jm",
39261         "1876"
39262       ],
39263       [
39264         "Japan (日本)",
39265         "jp",
39266         "81"
39267       ],
39268       [
39269         "Jersey",
39270         "je",
39271         "44",
39272         3
39273       ],
39274       [
39275         "Jordan (‫الأردن‬‎)",
39276         "jo",
39277         "962"
39278       ],
39279       [
39280         "Kazakhstan (Казахстан)",
39281         "kz",
39282         "7",
39283         1
39284       ],
39285       [
39286         "Kenya",
39287         "ke",
39288         "254"
39289       ],
39290       [
39291         "Kiribati",
39292         "ki",
39293         "686"
39294       ],
39295       [
39296         "Kosovo",
39297         "xk",
39298         "383"
39299       ],
39300       [
39301         "Kuwait (‫الكويت‬‎)",
39302         "kw",
39303         "965"
39304       ],
39305       [
39306         "Kyrgyzstan (Кыргызстан)",
39307         "kg",
39308         "996"
39309       ],
39310       [
39311         "Laos (ລາວ)",
39312         "la",
39313         "856"
39314       ],
39315       [
39316         "Latvia (Latvija)",
39317         "lv",
39318         "371"
39319       ],
39320       [
39321         "Lebanon (‫لبنان‬‎)",
39322         "lb",
39323         "961"
39324       ],
39325       [
39326         "Lesotho",
39327         "ls",
39328         "266"
39329       ],
39330       [
39331         "Liberia",
39332         "lr",
39333         "231"
39334       ],
39335       [
39336         "Libya (‫ليبيا‬‎)",
39337         "ly",
39338         "218"
39339       ],
39340       [
39341         "Liechtenstein",
39342         "li",
39343         "423"
39344       ],
39345       [
39346         "Lithuania (Lietuva)",
39347         "lt",
39348         "370"
39349       ],
39350       [
39351         "Luxembourg",
39352         "lu",
39353         "352"
39354       ],
39355       [
39356         "Macau (澳門)",
39357         "mo",
39358         "853"
39359       ],
39360       [
39361         "Macedonia (FYROM) (Македонија)",
39362         "mk",
39363         "389"
39364       ],
39365       [
39366         "Madagascar (Madagasikara)",
39367         "mg",
39368         "261"
39369       ],
39370       [
39371         "Malawi",
39372         "mw",
39373         "265"
39374       ],
39375       [
39376         "Malaysia",
39377         "my",
39378         "60"
39379       ],
39380       [
39381         "Maldives",
39382         "mv",
39383         "960"
39384       ],
39385       [
39386         "Mali",
39387         "ml",
39388         "223"
39389       ],
39390       [
39391         "Malta",
39392         "mt",
39393         "356"
39394       ],
39395       [
39396         "Marshall Islands",
39397         "mh",
39398         "692"
39399       ],
39400       [
39401         "Martinique",
39402         "mq",
39403         "596"
39404       ],
39405       [
39406         "Mauritania (‫موريتانيا‬‎)",
39407         "mr",
39408         "222"
39409       ],
39410       [
39411         "Mauritius (Moris)",
39412         "mu",
39413         "230"
39414       ],
39415       [
39416         "Mayotte",
39417         "yt",
39418         "262",
39419         1
39420       ],
39421       [
39422         "Mexico (México)",
39423         "mx",
39424         "52"
39425       ],
39426       [
39427         "Micronesia",
39428         "fm",
39429         "691"
39430       ],
39431       [
39432         "Moldova (Republica Moldova)",
39433         "md",
39434         "373"
39435       ],
39436       [
39437         "Monaco",
39438         "mc",
39439         "377"
39440       ],
39441       [
39442         "Mongolia (Монгол)",
39443         "mn",
39444         "976"
39445       ],
39446       [
39447         "Montenegro (Crna Gora)",
39448         "me",
39449         "382"
39450       ],
39451       [
39452         "Montserrat",
39453         "ms",
39454         "1664"
39455       ],
39456       [
39457         "Morocco (‫المغرب‬‎)",
39458         "ma",
39459         "212",
39460         0
39461       ],
39462       [
39463         "Mozambique (Moçambique)",
39464         "mz",
39465         "258"
39466       ],
39467       [
39468         "Myanmar (Burma) (မြန်မာ)",
39469         "mm",
39470         "95"
39471       ],
39472       [
39473         "Namibia (Namibië)",
39474         "na",
39475         "264"
39476       ],
39477       [
39478         "Nauru",
39479         "nr",
39480         "674"
39481       ],
39482       [
39483         "Nepal (नेपाल)",
39484         "np",
39485         "977"
39486       ],
39487       [
39488         "Netherlands (Nederland)",
39489         "nl",
39490         "31"
39491       ],
39492       [
39493         "New Caledonia (Nouvelle-Calédonie)",
39494         "nc",
39495         "687"
39496       ],
39497       [
39498         "New Zealand",
39499         "nz",
39500         "64"
39501       ],
39502       [
39503         "Nicaragua",
39504         "ni",
39505         "505"
39506       ],
39507       [
39508         "Niger (Nijar)",
39509         "ne",
39510         "227"
39511       ],
39512       [
39513         "Nigeria",
39514         "ng",
39515         "234"
39516       ],
39517       [
39518         "Niue",
39519         "nu",
39520         "683"
39521       ],
39522       [
39523         "Norfolk Island",
39524         "nf",
39525         "672"
39526       ],
39527       [
39528         "North Korea (조선 민주주의 인민 공화국)",
39529         "kp",
39530         "850"
39531       ],
39532       [
39533         "Northern Mariana Islands",
39534         "mp",
39535         "1670"
39536       ],
39537       [
39538         "Norway (Norge)",
39539         "no",
39540         "47",
39541         0
39542       ],
39543       [
39544         "Oman (‫عُمان‬‎)",
39545         "om",
39546         "968"
39547       ],
39548       [
39549         "Pakistan (‫پاکستان‬‎)",
39550         "pk",
39551         "92"
39552       ],
39553       [
39554         "Palau",
39555         "pw",
39556         "680"
39557       ],
39558       [
39559         "Palestine (‫فلسطين‬‎)",
39560         "ps",
39561         "970"
39562       ],
39563       [
39564         "Panama (Panamá)",
39565         "pa",
39566         "507"
39567       ],
39568       [
39569         "Papua New Guinea",
39570         "pg",
39571         "675"
39572       ],
39573       [
39574         "Paraguay",
39575         "py",
39576         "595"
39577       ],
39578       [
39579         "Peru (Perú)",
39580         "pe",
39581         "51"
39582       ],
39583       [
39584         "Philippines",
39585         "ph",
39586         "63"
39587       ],
39588       [
39589         "Poland (Polska)",
39590         "pl",
39591         "48"
39592       ],
39593       [
39594         "Portugal",
39595         "pt",
39596         "351"
39597       ],
39598       [
39599         "Puerto Rico",
39600         "pr",
39601         "1",
39602         3,
39603         ["787", "939"]
39604       ],
39605       [
39606         "Qatar (‫قطر‬‎)",
39607         "qa",
39608         "974"
39609       ],
39610       [
39611         "Réunion (La Réunion)",
39612         "re",
39613         "262",
39614         0
39615       ],
39616       [
39617         "Romania (România)",
39618         "ro",
39619         "40"
39620       ],
39621       [
39622         "Russia (Россия)",
39623         "ru",
39624         "7",
39625         0
39626       ],
39627       [
39628         "Rwanda",
39629         "rw",
39630         "250"
39631       ],
39632       [
39633         "Saint Barthélemy",
39634         "bl",
39635         "590",
39636         1
39637       ],
39638       [
39639         "Saint Helena",
39640         "sh",
39641         "290"
39642       ],
39643       [
39644         "Saint Kitts and Nevis",
39645         "kn",
39646         "1869"
39647       ],
39648       [
39649         "Saint Lucia",
39650         "lc",
39651         "1758"
39652       ],
39653       [
39654         "Saint Martin (Saint-Martin (partie française))",
39655         "mf",
39656         "590",
39657         2
39658       ],
39659       [
39660         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39661         "pm",
39662         "508"
39663       ],
39664       [
39665         "Saint Vincent and the Grenadines",
39666         "vc",
39667         "1784"
39668       ],
39669       [
39670         "Samoa",
39671         "ws",
39672         "685"
39673       ],
39674       [
39675         "San Marino",
39676         "sm",
39677         "378"
39678       ],
39679       [
39680         "São Tomé and Príncipe (São Tomé e Príncipe)",
39681         "st",
39682         "239"
39683       ],
39684       [
39685         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39686         "sa",
39687         "966"
39688       ],
39689       [
39690         "Senegal (Sénégal)",
39691         "sn",
39692         "221"
39693       ],
39694       [
39695         "Serbia (Србија)",
39696         "rs",
39697         "381"
39698       ],
39699       [
39700         "Seychelles",
39701         "sc",
39702         "248"
39703       ],
39704       [
39705         "Sierra Leone",
39706         "sl",
39707         "232"
39708       ],
39709       [
39710         "Singapore",
39711         "sg",
39712         "65"
39713       ],
39714       [
39715         "Sint Maarten",
39716         "sx",
39717         "1721"
39718       ],
39719       [
39720         "Slovakia (Slovensko)",
39721         "sk",
39722         "421"
39723       ],
39724       [
39725         "Slovenia (Slovenija)",
39726         "si",
39727         "386"
39728       ],
39729       [
39730         "Solomon Islands",
39731         "sb",
39732         "677"
39733       ],
39734       [
39735         "Somalia (Soomaaliya)",
39736         "so",
39737         "252"
39738       ],
39739       [
39740         "South Africa",
39741         "za",
39742         "27"
39743       ],
39744       [
39745         "South Korea (대한민국)",
39746         "kr",
39747         "82"
39748       ],
39749       [
39750         "South Sudan (‫جنوب السودان‬‎)",
39751         "ss",
39752         "211"
39753       ],
39754       [
39755         "Spain (España)",
39756         "es",
39757         "34"
39758       ],
39759       [
39760         "Sri Lanka (ශ්‍රී ලංකාව)",
39761         "lk",
39762         "94"
39763       ],
39764       [
39765         "Sudan (‫السودان‬‎)",
39766         "sd",
39767         "249"
39768       ],
39769       [
39770         "Suriname",
39771         "sr",
39772         "597"
39773       ],
39774       [
39775         "Svalbard and Jan Mayen",
39776         "sj",
39777         "47",
39778         1
39779       ],
39780       [
39781         "Swaziland",
39782         "sz",
39783         "268"
39784       ],
39785       [
39786         "Sweden (Sverige)",
39787         "se",
39788         "46"
39789       ],
39790       [
39791         "Switzerland (Schweiz)",
39792         "ch",
39793         "41"
39794       ],
39795       [
39796         "Syria (‫سوريا‬‎)",
39797         "sy",
39798         "963"
39799       ],
39800       [
39801         "Taiwan (台灣)",
39802         "tw",
39803         "886"
39804       ],
39805       [
39806         "Tajikistan",
39807         "tj",
39808         "992"
39809       ],
39810       [
39811         "Tanzania",
39812         "tz",
39813         "255"
39814       ],
39815       [
39816         "Thailand (ไทย)",
39817         "th",
39818         "66"
39819       ],
39820       [
39821         "Timor-Leste",
39822         "tl",
39823         "670"
39824       ],
39825       [
39826         "Togo",
39827         "tg",
39828         "228"
39829       ],
39830       [
39831         "Tokelau",
39832         "tk",
39833         "690"
39834       ],
39835       [
39836         "Tonga",
39837         "to",
39838         "676"
39839       ],
39840       [
39841         "Trinidad and Tobago",
39842         "tt",
39843         "1868"
39844       ],
39845       [
39846         "Tunisia (‫تونس‬‎)",
39847         "tn",
39848         "216"
39849       ],
39850       [
39851         "Turkey (Türkiye)",
39852         "tr",
39853         "90"
39854       ],
39855       [
39856         "Turkmenistan",
39857         "tm",
39858         "993"
39859       ],
39860       [
39861         "Turks and Caicos Islands",
39862         "tc",
39863         "1649"
39864       ],
39865       [
39866         "Tuvalu",
39867         "tv",
39868         "688"
39869       ],
39870       [
39871         "U.S. Virgin Islands",
39872         "vi",
39873         "1340"
39874       ],
39875       [
39876         "Uganda",
39877         "ug",
39878         "256"
39879       ],
39880       [
39881         "Ukraine (Україна)",
39882         "ua",
39883         "380"
39884       ],
39885       [
39886         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39887         "ae",
39888         "971"
39889       ],
39890       [
39891         "United Kingdom",
39892         "gb",
39893         "44",
39894         0
39895       ],
39896       [
39897         "United States",
39898         "us",
39899         "1",
39900         0
39901       ],
39902       [
39903         "Uruguay",
39904         "uy",
39905         "598"
39906       ],
39907       [
39908         "Uzbekistan (Oʻzbekiston)",
39909         "uz",
39910         "998"
39911       ],
39912       [
39913         "Vanuatu",
39914         "vu",
39915         "678"
39916       ],
39917       [
39918         "Vatican City (Città del Vaticano)",
39919         "va",
39920         "39",
39921         1
39922       ],
39923       [
39924         "Venezuela",
39925         "ve",
39926         "58"
39927       ],
39928       [
39929         "Vietnam (Việt Nam)",
39930         "vn",
39931         "84"
39932       ],
39933       [
39934         "Wallis and Futuna (Wallis-et-Futuna)",
39935         "wf",
39936         "681"
39937       ],
39938       [
39939         "Western Sahara (‫الصحراء الغربية‬‎)",
39940         "eh",
39941         "212",
39942         1
39943       ],
39944       [
39945         "Yemen (‫اليمن‬‎)",
39946         "ye",
39947         "967"
39948       ],
39949       [
39950         "Zambia",
39951         "zm",
39952         "260"
39953       ],
39954       [
39955         "Zimbabwe",
39956         "zw",
39957         "263"
39958       ],
39959       [
39960         "Åland Islands",
39961         "ax",
39962         "358",
39963         1
39964       ]
39965   ];
39966   
39967   return d;
39968 }/**
39969 *    This script refer to:
39970 *    Title: International Telephone Input
39971 *    Author: Jack O'Connor
39972 *    Code version:  v12.1.12
39973 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39974 **/
39975
39976 /**
39977  * @class Roo.bootstrap.PhoneInput
39978  * @extends Roo.bootstrap.TriggerField
39979  * An input with International dial-code selection
39980  
39981  * @cfg {String} defaultDialCode default '+852'
39982  * @cfg {Array} preferedCountries default []
39983   
39984  * @constructor
39985  * Create a new PhoneInput.
39986  * @param {Object} config Configuration options
39987  */
39988
39989 Roo.bootstrap.PhoneInput = function(config) {
39990     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39991 };
39992
39993 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39994         
39995         listWidth: undefined,
39996         
39997         selectedClass: 'active',
39998         
39999         invalidClass : "has-warning",
40000         
40001         validClass: 'has-success',
40002         
40003         allowed: '0123456789',
40004         
40005         max_length: 15,
40006         
40007         /**
40008          * @cfg {String} defaultDialCode The default dial code when initializing the input
40009          */
40010         defaultDialCode: '+852',
40011         
40012         /**
40013          * @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
40014          */
40015         preferedCountries: false,
40016         
40017         getAutoCreate : function()
40018         {
40019             var data = Roo.bootstrap.PhoneInputData();
40020             var align = this.labelAlign || this.parentLabelAlign();
40021             var id = Roo.id();
40022             
40023             this.allCountries = [];
40024             this.dialCodeMapping = [];
40025             
40026             for (var i = 0; i < data.length; i++) {
40027               var c = data[i];
40028               this.allCountries[i] = {
40029                 name: c[0],
40030                 iso2: c[1],
40031                 dialCode: c[2],
40032                 priority: c[3] || 0,
40033                 areaCodes: c[4] || null
40034               };
40035               this.dialCodeMapping[c[2]] = {
40036                   name: c[0],
40037                   iso2: c[1],
40038                   priority: c[3] || 0,
40039                   areaCodes: c[4] || null
40040               };
40041             }
40042             
40043             var cfg = {
40044                 cls: 'form-group',
40045                 cn: []
40046             };
40047             
40048             var input =  {
40049                 tag: 'input',
40050                 id : id,
40051                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40052                 maxlength: this.max_length,
40053                 cls : 'form-control tel-input',
40054                 autocomplete: 'new-password'
40055             };
40056             
40057             var hiddenInput = {
40058                 tag: 'input',
40059                 type: 'hidden',
40060                 cls: 'hidden-tel-input'
40061             };
40062             
40063             if (this.name) {
40064                 hiddenInput.name = this.name;
40065             }
40066             
40067             if (this.disabled) {
40068                 input.disabled = true;
40069             }
40070             
40071             var flag_container = {
40072                 tag: 'div',
40073                 cls: 'flag-box',
40074                 cn: [
40075                     {
40076                         tag: 'div',
40077                         cls: 'flag'
40078                     },
40079                     {
40080                         tag: 'div',
40081                         cls: 'caret'
40082                     }
40083                 ]
40084             };
40085             
40086             var box = {
40087                 tag: 'div',
40088                 cls: this.hasFeedback ? 'has-feedback' : '',
40089                 cn: [
40090                     hiddenInput,
40091                     input,
40092                     {
40093                         tag: 'input',
40094                         cls: 'dial-code-holder',
40095                         disabled: true
40096                     }
40097                 ]
40098             };
40099             
40100             var container = {
40101                 cls: 'roo-select2-container input-group',
40102                 cn: [
40103                     flag_container,
40104                     box
40105                 ]
40106             };
40107             
40108             if (this.fieldLabel.length) {
40109                 var indicator = {
40110                     tag: 'i',
40111                     tooltip: 'This field is required'
40112                 };
40113                 
40114                 var label = {
40115                     tag: 'label',
40116                     'for':  id,
40117                     cls: 'control-label',
40118                     cn: []
40119                 };
40120                 
40121                 var label_text = {
40122                     tag: 'span',
40123                     html: this.fieldLabel
40124                 };
40125                 
40126                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40127                 label.cn = [
40128                     indicator,
40129                     label_text
40130                 ];
40131                 
40132                 if(this.indicatorpos == 'right') {
40133                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40134                     label.cn = [
40135                         label_text,
40136                         indicator
40137                     ];
40138                 }
40139                 
40140                 if(align == 'left') {
40141                     container = {
40142                         tag: 'div',
40143                         cn: [
40144                             container
40145                         ]
40146                     };
40147                     
40148                     if(this.labelWidth > 12){
40149                         label.style = "width: " + this.labelWidth + 'px';
40150                     }
40151                     if(this.labelWidth < 13 && this.labelmd == 0){
40152                         this.labelmd = this.labelWidth;
40153                     }
40154                     if(this.labellg > 0){
40155                         label.cls += ' col-lg-' + this.labellg;
40156                         input.cls += ' col-lg-' + (12 - this.labellg);
40157                     }
40158                     if(this.labelmd > 0){
40159                         label.cls += ' col-md-' + this.labelmd;
40160                         container.cls += ' col-md-' + (12 - this.labelmd);
40161                     }
40162                     if(this.labelsm > 0){
40163                         label.cls += ' col-sm-' + this.labelsm;
40164                         container.cls += ' col-sm-' + (12 - this.labelsm);
40165                     }
40166                     if(this.labelxs > 0){
40167                         label.cls += ' col-xs-' + this.labelxs;
40168                         container.cls += ' col-xs-' + (12 - this.labelxs);
40169                     }
40170                 }
40171             }
40172             
40173             cfg.cn = [
40174                 label,
40175                 container
40176             ];
40177             
40178             var settings = this;
40179             
40180             ['xs','sm','md','lg'].map(function(size){
40181                 if (settings[size]) {
40182                     cfg.cls += ' col-' + size + '-' + settings[size];
40183                 }
40184             });
40185             
40186             this.store = new Roo.data.Store({
40187                 proxy : new Roo.data.MemoryProxy({}),
40188                 reader : new Roo.data.JsonReader({
40189                     fields : [
40190                         {
40191                             'name' : 'name',
40192                             'type' : 'string'
40193                         },
40194                         {
40195                             'name' : 'iso2',
40196                             'type' : 'string'
40197                         },
40198                         {
40199                             'name' : 'dialCode',
40200                             'type' : 'string'
40201                         },
40202                         {
40203                             'name' : 'priority',
40204                             'type' : 'string'
40205                         },
40206                         {
40207                             'name' : 'areaCodes',
40208                             'type' : 'string'
40209                         }
40210                     ]
40211                 })
40212             });
40213             
40214             if(!this.preferedCountries) {
40215                 this.preferedCountries = [
40216                     'hk',
40217                     'gb',
40218                     'us'
40219                 ];
40220             }
40221             
40222             var p = this.preferedCountries.reverse();
40223             
40224             if(p) {
40225                 for (var i = 0; i < p.length; i++) {
40226                     for (var j = 0; j < this.allCountries.length; j++) {
40227                         if(this.allCountries[j].iso2 == p[i]) {
40228                             var t = this.allCountries[j];
40229                             this.allCountries.splice(j,1);
40230                             this.allCountries.unshift(t);
40231                         }
40232                     } 
40233                 }
40234             }
40235             
40236             this.store.proxy.data = {
40237                 success: true,
40238                 data: this.allCountries
40239             };
40240             
40241             return cfg;
40242         },
40243         
40244         initEvents : function()
40245         {
40246             this.createList();
40247             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40248             
40249             this.indicator = this.indicatorEl();
40250             this.flag = this.flagEl();
40251             this.dialCodeHolder = this.dialCodeHolderEl();
40252             
40253             this.trigger = this.el.select('div.flag-box',true).first();
40254             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40255             
40256             var _this = this;
40257             
40258             (function(){
40259                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40260                 _this.list.setWidth(lw);
40261             }).defer(100);
40262             
40263             this.list.on('mouseover', this.onViewOver, this);
40264             this.list.on('mousemove', this.onViewMove, this);
40265             this.inputEl().on("keyup", this.onKeyUp, this);
40266             this.inputEl().on("keypress", this.onKeyPress, this);
40267             
40268             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40269
40270             this.view = new Roo.View(this.list, this.tpl, {
40271                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40272             });
40273             
40274             this.view.on('click', this.onViewClick, this);
40275             this.setValue(this.defaultDialCode);
40276         },
40277         
40278         onTriggerClick : function(e)
40279         {
40280             Roo.log('trigger click');
40281             if(this.disabled){
40282                 return;
40283             }
40284             
40285             if(this.isExpanded()){
40286                 this.collapse();
40287                 this.hasFocus = false;
40288             }else {
40289                 this.store.load({});
40290                 this.hasFocus = true;
40291                 this.expand();
40292             }
40293         },
40294         
40295         isExpanded : function()
40296         {
40297             return this.list.isVisible();
40298         },
40299         
40300         collapse : function()
40301         {
40302             if(!this.isExpanded()){
40303                 return;
40304             }
40305             this.list.hide();
40306             Roo.get(document).un('mousedown', this.collapseIf, this);
40307             Roo.get(document).un('mousewheel', this.collapseIf, this);
40308             this.fireEvent('collapse', this);
40309             this.validate();
40310         },
40311         
40312         expand : function()
40313         {
40314             Roo.log('expand');
40315
40316             if(this.isExpanded() || !this.hasFocus){
40317                 return;
40318             }
40319             
40320             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40321             this.list.setWidth(lw);
40322             
40323             this.list.show();
40324             this.restrictHeight();
40325             
40326             Roo.get(document).on('mousedown', this.collapseIf, this);
40327             Roo.get(document).on('mousewheel', this.collapseIf, this);
40328             
40329             this.fireEvent('expand', this);
40330         },
40331         
40332         restrictHeight : function()
40333         {
40334             this.list.alignTo(this.inputEl(), this.listAlign);
40335             this.list.alignTo(this.inputEl(), this.listAlign);
40336         },
40337         
40338         onViewOver : function(e, t)
40339         {
40340             if(this.inKeyMode){
40341                 return;
40342             }
40343             var item = this.view.findItemFromChild(t);
40344             
40345             if(item){
40346                 var index = this.view.indexOf(item);
40347                 this.select(index, false);
40348             }
40349         },
40350
40351         // private
40352         onViewClick : function(view, doFocus, el, e)
40353         {
40354             var index = this.view.getSelectedIndexes()[0];
40355             
40356             var r = this.store.getAt(index);
40357             
40358             if(r){
40359                 this.onSelect(r, index);
40360             }
40361             if(doFocus !== false && !this.blockFocus){
40362                 this.inputEl().focus();
40363             }
40364         },
40365         
40366         onViewMove : function(e, t)
40367         {
40368             this.inKeyMode = false;
40369         },
40370         
40371         select : function(index, scrollIntoView)
40372         {
40373             this.selectedIndex = index;
40374             this.view.select(index);
40375             if(scrollIntoView !== false){
40376                 var el = this.view.getNode(index);
40377                 if(el){
40378                     this.list.scrollChildIntoView(el, false);
40379                 }
40380             }
40381         },
40382         
40383         createList : function()
40384         {
40385             this.list = Roo.get(document.body).createChild({
40386                 tag: 'ul',
40387                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40388                 style: 'display:none'
40389             });
40390             
40391             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40392         },
40393         
40394         collapseIf : function(e)
40395         {
40396             var in_combo  = e.within(this.el);
40397             var in_list =  e.within(this.list);
40398             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40399             
40400             if (in_combo || in_list || is_list) {
40401                 return;
40402             }
40403             this.collapse();
40404         },
40405         
40406         onSelect : function(record, index)
40407         {
40408             if(this.fireEvent('beforeselect', this, record, index) !== false){
40409                 
40410                 this.setFlagClass(record.data.iso2);
40411                 this.setDialCode(record.data.dialCode);
40412                 this.hasFocus = false;
40413                 this.collapse();
40414                 this.fireEvent('select', this, record, index);
40415             }
40416         },
40417         
40418         flagEl : function()
40419         {
40420             var flag = this.el.select('div.flag',true).first();
40421             if(!flag){
40422                 return false;
40423             }
40424             return flag;
40425         },
40426         
40427         dialCodeHolderEl : function()
40428         {
40429             var d = this.el.select('input.dial-code-holder',true).first();
40430             if(!d){
40431                 return false;
40432             }
40433             return d;
40434         },
40435         
40436         setDialCode : function(v)
40437         {
40438             this.dialCodeHolder.dom.value = '+'+v;
40439         },
40440         
40441         setFlagClass : function(n)
40442         {
40443             this.flag.dom.className = 'flag '+n;
40444         },
40445         
40446         getValue : function()
40447         {
40448             var v = this.inputEl().getValue();
40449             if(this.dialCodeHolder) {
40450                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40451             }
40452             return v;
40453         },
40454         
40455         setValue : function(v)
40456         {
40457             var d = this.getDialCode(v);
40458             
40459             //invalid dial code
40460             if(v.length == 0 || !d || d.length == 0) {
40461                 if(this.rendered){
40462                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40463                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40464                 }
40465                 return;
40466             }
40467             
40468             //valid dial code
40469             this.setFlagClass(this.dialCodeMapping[d].iso2);
40470             this.setDialCode(d);
40471             this.inputEl().dom.value = v.replace('+'+d,'');
40472             this.hiddenEl().dom.value = this.getValue();
40473             
40474             this.validate();
40475         },
40476         
40477         getDialCode : function(v)
40478         {
40479             v = v ||  '';
40480             
40481             if (v.length == 0) {
40482                 return this.dialCodeHolder.dom.value;
40483             }
40484             
40485             var dialCode = "";
40486             if (v.charAt(0) != "+") {
40487                 return false;
40488             }
40489             var numericChars = "";
40490             for (var i = 1; i < v.length; i++) {
40491               var c = v.charAt(i);
40492               if (!isNaN(c)) {
40493                 numericChars += c;
40494                 if (this.dialCodeMapping[numericChars]) {
40495                   dialCode = v.substr(1, i);
40496                 }
40497                 if (numericChars.length == 4) {
40498                   break;
40499                 }
40500               }
40501             }
40502             return dialCode;
40503         },
40504         
40505         reset : function()
40506         {
40507             this.setValue(this.defaultDialCode);
40508             this.validate();
40509         },
40510         
40511         hiddenEl : function()
40512         {
40513             return this.el.select('input.hidden-tel-input',true).first();
40514         },
40515         
40516         // after setting val
40517         onKeyUp : function(e){
40518             this.setValue(this.getValue());
40519         },
40520         
40521         onKeyPress : function(e){
40522             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40523                 e.stopEvent();
40524             }
40525         }
40526         
40527 });
40528 /**
40529  * @class Roo.bootstrap.MoneyField
40530  * @extends Roo.bootstrap.ComboBox
40531  * Bootstrap MoneyField class
40532  * 
40533  * @constructor
40534  * Create a new MoneyField.
40535  * @param {Object} config Configuration options
40536  */
40537
40538 Roo.bootstrap.MoneyField = function(config) {
40539     
40540     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40541     
40542 };
40543
40544 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40545     
40546     /**
40547      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40548      */
40549     allowDecimals : true,
40550     /**
40551      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40552      */
40553     decimalSeparator : ".",
40554     /**
40555      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40556      */
40557     decimalPrecision : 0,
40558     /**
40559      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40560      */
40561     allowNegative : true,
40562     /**
40563      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40564      */
40565     allowZero: true,
40566     /**
40567      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40568      */
40569     minValue : Number.NEGATIVE_INFINITY,
40570     /**
40571      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40572      */
40573     maxValue : Number.MAX_VALUE,
40574     /**
40575      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40576      */
40577     minText : "The minimum value for this field is {0}",
40578     /**
40579      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40580      */
40581     maxText : "The maximum value for this field is {0}",
40582     /**
40583      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40584      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40585      */
40586     nanText : "{0} is not a valid number",
40587     /**
40588      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40589      */
40590     castInt : true,
40591     /**
40592      * @cfg {String} defaults currency of the MoneyField
40593      * value should be in lkey
40594      */
40595     defaultCurrency : false,
40596     /**
40597      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40598      */
40599     thousandsDelimiter : false,
40600     /**
40601      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40602      */
40603     max_length: false,
40604     
40605     inputlg : 9,
40606     inputmd : 9,
40607     inputsm : 9,
40608     inputxs : 6,
40609     
40610     store : false,
40611     
40612     getAutoCreate : function()
40613     {
40614         var align = this.labelAlign || this.parentLabelAlign();
40615         
40616         var id = Roo.id();
40617
40618         var cfg = {
40619             cls: 'form-group',
40620             cn: []
40621         };
40622
40623         var input =  {
40624             tag: 'input',
40625             id : id,
40626             cls : 'form-control roo-money-amount-input',
40627             autocomplete: 'new-password'
40628         };
40629         
40630         var hiddenInput = {
40631             tag: 'input',
40632             type: 'hidden',
40633             id: Roo.id(),
40634             cls: 'hidden-number-input'
40635         };
40636         
40637         if(this.max_length) {
40638             input.maxlength = this.max_length; 
40639         }
40640         
40641         if (this.name) {
40642             hiddenInput.name = this.name;
40643         }
40644
40645         if (this.disabled) {
40646             input.disabled = true;
40647         }
40648
40649         var clg = 12 - this.inputlg;
40650         var cmd = 12 - this.inputmd;
40651         var csm = 12 - this.inputsm;
40652         var cxs = 12 - this.inputxs;
40653         
40654         var container = {
40655             tag : 'div',
40656             cls : 'row roo-money-field',
40657             cn : [
40658                 {
40659                     tag : 'div',
40660                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40661                     cn : [
40662                         {
40663                             tag : 'div',
40664                             cls: 'roo-select2-container input-group',
40665                             cn: [
40666                                 {
40667                                     tag : 'input',
40668                                     cls : 'form-control roo-money-currency-input',
40669                                     autocomplete: 'new-password',
40670                                     readOnly : 1,
40671                                     name : this.currencyName
40672                                 },
40673                                 {
40674                                     tag :'span',
40675                                     cls : 'input-group-addon',
40676                                     cn : [
40677                                         {
40678                                             tag: 'span',
40679                                             cls: 'caret'
40680                                         }
40681                                     ]
40682                                 }
40683                             ]
40684                         }
40685                     ]
40686                 },
40687                 {
40688                     tag : 'div',
40689                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40690                     cn : [
40691                         {
40692                             tag: 'div',
40693                             cls: this.hasFeedback ? 'has-feedback' : '',
40694                             cn: [
40695                                 input
40696                             ]
40697                         }
40698                     ]
40699                 }
40700             ]
40701             
40702         };
40703         
40704         if (this.fieldLabel.length) {
40705             var indicator = {
40706                 tag: 'i',
40707                 tooltip: 'This field is required'
40708             };
40709
40710             var label = {
40711                 tag: 'label',
40712                 'for':  id,
40713                 cls: 'control-label',
40714                 cn: []
40715             };
40716
40717             var label_text = {
40718                 tag: 'span',
40719                 html: this.fieldLabel
40720             };
40721
40722             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40723             label.cn = [
40724                 indicator,
40725                 label_text
40726             ];
40727
40728             if(this.indicatorpos == 'right') {
40729                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40730                 label.cn = [
40731                     label_text,
40732                     indicator
40733                 ];
40734             }
40735
40736             if(align == 'left') {
40737                 container = {
40738                     tag: 'div',
40739                     cn: [
40740                         container
40741                     ]
40742                 };
40743
40744                 if(this.labelWidth > 12){
40745                     label.style = "width: " + this.labelWidth + 'px';
40746                 }
40747                 if(this.labelWidth < 13 && this.labelmd == 0){
40748                     this.labelmd = this.labelWidth;
40749                 }
40750                 if(this.labellg > 0){
40751                     label.cls += ' col-lg-' + this.labellg;
40752                     input.cls += ' col-lg-' + (12 - this.labellg);
40753                 }
40754                 if(this.labelmd > 0){
40755                     label.cls += ' col-md-' + this.labelmd;
40756                     container.cls += ' col-md-' + (12 - this.labelmd);
40757                 }
40758                 if(this.labelsm > 0){
40759                     label.cls += ' col-sm-' + this.labelsm;
40760                     container.cls += ' col-sm-' + (12 - this.labelsm);
40761                 }
40762                 if(this.labelxs > 0){
40763                     label.cls += ' col-xs-' + this.labelxs;
40764                     container.cls += ' col-xs-' + (12 - this.labelxs);
40765                 }
40766             }
40767         }
40768
40769         cfg.cn = [
40770             label,
40771             container,
40772             hiddenInput
40773         ];
40774         
40775         var settings = this;
40776
40777         ['xs','sm','md','lg'].map(function(size){
40778             if (settings[size]) {
40779                 cfg.cls += ' col-' + size + '-' + settings[size];
40780             }
40781         });
40782         
40783         return cfg;
40784     },
40785     
40786     initEvents : function()
40787     {
40788         this.indicator = this.indicatorEl();
40789         
40790         this.initCurrencyEvent();
40791         
40792         this.initNumberEvent();
40793     },
40794     
40795     initCurrencyEvent : function()
40796     {
40797         if (!this.store) {
40798             throw "can not find store for combo";
40799         }
40800         
40801         this.store = Roo.factory(this.store, Roo.data);
40802         this.store.parent = this;
40803         
40804         this.createList();
40805         
40806         this.triggerEl = this.el.select('.input-group-addon', true).first();
40807         
40808         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40809         
40810         var _this = this;
40811         
40812         (function(){
40813             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40814             _this.list.setWidth(lw);
40815         }).defer(100);
40816         
40817         this.list.on('mouseover', this.onViewOver, this);
40818         this.list.on('mousemove', this.onViewMove, this);
40819         this.list.on('scroll', this.onViewScroll, this);
40820         
40821         if(!this.tpl){
40822             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40823         }
40824         
40825         this.view = new Roo.View(this.list, this.tpl, {
40826             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40827         });
40828         
40829         this.view.on('click', this.onViewClick, this);
40830         
40831         this.store.on('beforeload', this.onBeforeLoad, this);
40832         this.store.on('load', this.onLoad, this);
40833         this.store.on('loadexception', this.onLoadException, this);
40834         
40835         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40836             "up" : function(e){
40837                 this.inKeyMode = true;
40838                 this.selectPrev();
40839             },
40840
40841             "down" : function(e){
40842                 if(!this.isExpanded()){
40843                     this.onTriggerClick();
40844                 }else{
40845                     this.inKeyMode = true;
40846                     this.selectNext();
40847                 }
40848             },
40849
40850             "enter" : function(e){
40851                 this.collapse();
40852                 
40853                 if(this.fireEvent("specialkey", this, e)){
40854                     this.onViewClick(false);
40855                 }
40856                 
40857                 return true;
40858             },
40859
40860             "esc" : function(e){
40861                 this.collapse();
40862             },
40863
40864             "tab" : function(e){
40865                 this.collapse();
40866                 
40867                 if(this.fireEvent("specialkey", this, e)){
40868                     this.onViewClick(false);
40869                 }
40870                 
40871                 return true;
40872             },
40873
40874             scope : this,
40875
40876             doRelay : function(foo, bar, hname){
40877                 if(hname == 'down' || this.scope.isExpanded()){
40878                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40879                 }
40880                 return true;
40881             },
40882
40883             forceKeyDown: true
40884         });
40885         
40886         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40887         
40888     },
40889     
40890     initNumberEvent : function(e)
40891     {
40892         this.inputEl().on("keydown" , this.fireKey,  this);
40893         this.inputEl().on("focus", this.onFocus,  this);
40894         this.inputEl().on("blur", this.onBlur,  this);
40895         
40896         this.inputEl().relayEvent('keyup', this);
40897         
40898         if(this.indicator){
40899             this.indicator.addClass('invisible');
40900         }
40901  
40902         this.originalValue = this.getValue();
40903         
40904         if(this.validationEvent == 'keyup'){
40905             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40906             this.inputEl().on('keyup', this.filterValidation, this);
40907         }
40908         else if(this.validationEvent !== false){
40909             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40910         }
40911         
40912         if(this.selectOnFocus){
40913             this.on("focus", this.preFocus, this);
40914             
40915         }
40916         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40917             this.inputEl().on("keypress", this.filterKeys, this);
40918         } else {
40919             this.inputEl().relayEvent('keypress', this);
40920         }
40921         
40922         var allowed = "0123456789";
40923         
40924         if(this.allowDecimals){
40925             allowed += this.decimalSeparator;
40926         }
40927         
40928         if(this.allowNegative){
40929             allowed += "-";
40930         }
40931         
40932         if(this.thousandsDelimiter) {
40933             allowed += ",";
40934         }
40935         
40936         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40937         
40938         var keyPress = function(e){
40939             
40940             var k = e.getKey();
40941             
40942             var c = e.getCharCode();
40943             
40944             if(
40945                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40946                     allowed.indexOf(String.fromCharCode(c)) === -1
40947             ){
40948                 e.stopEvent();
40949                 return;
40950             }
40951             
40952             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40953                 return;
40954             }
40955             
40956             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40957                 e.stopEvent();
40958             }
40959         };
40960         
40961         this.inputEl().on("keypress", keyPress, this);
40962         
40963     },
40964     
40965     onTriggerClick : function(e)
40966     {   
40967         if(this.disabled){
40968             return;
40969         }
40970         
40971         this.page = 0;
40972         this.loadNext = false;
40973         
40974         if(this.isExpanded()){
40975             this.collapse();
40976             return;
40977         }
40978         
40979         this.hasFocus = true;
40980         
40981         if(this.triggerAction == 'all') {
40982             this.doQuery(this.allQuery, true);
40983             return;
40984         }
40985         
40986         this.doQuery(this.getRawValue());
40987     },
40988     
40989     getCurrency : function()
40990     {   
40991         var v = this.currencyEl().getValue();
40992         
40993         return v;
40994     },
40995     
40996     restrictHeight : function()
40997     {
40998         this.list.alignTo(this.currencyEl(), this.listAlign);
40999         this.list.alignTo(this.currencyEl(), this.listAlign);
41000     },
41001     
41002     onViewClick : function(view, doFocus, el, e)
41003     {
41004         var index = this.view.getSelectedIndexes()[0];
41005         
41006         var r = this.store.getAt(index);
41007         
41008         if(r){
41009             this.onSelect(r, index);
41010         }
41011     },
41012     
41013     onSelect : function(record, index){
41014         
41015         if(this.fireEvent('beforeselect', this, record, index) !== false){
41016         
41017             this.setFromCurrencyData(index > -1 ? record.data : false);
41018             
41019             this.collapse();
41020             
41021             this.fireEvent('select', this, record, index);
41022         }
41023     },
41024     
41025     setFromCurrencyData : function(o)
41026     {
41027         var currency = '';
41028         
41029         this.lastCurrency = o;
41030         
41031         if (this.currencyField) {
41032             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41033         } else {
41034             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41035         }
41036         
41037         this.lastSelectionText = currency;
41038         
41039         //setting default currency
41040         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41041             this.setCurrency(this.defaultCurrency);
41042             return;
41043         }
41044         
41045         this.setCurrency(currency);
41046     },
41047     
41048     setFromData : function(o)
41049     {
41050         var c = {};
41051         
41052         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41053         
41054         this.setFromCurrencyData(c);
41055         
41056         var value = '';
41057         
41058         if (this.name) {
41059             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41060         } else {
41061             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41062         }
41063         
41064         this.setValue(value);
41065         
41066     },
41067     
41068     setCurrency : function(v)
41069     {   
41070         this.currencyValue = v;
41071         
41072         if(this.rendered){
41073             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41074             this.validate();
41075         }
41076     },
41077     
41078     setValue : function(v)
41079     {
41080         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41081         
41082         this.value = v;
41083         
41084         if(this.rendered){
41085             
41086             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41087             
41088             this.inputEl().dom.value = (v == '') ? '' :
41089                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41090             
41091             if(!this.allowZero && v === '0') {
41092                 this.hiddenEl().dom.value = '';
41093                 this.inputEl().dom.value = '';
41094             }
41095             
41096             this.validate();
41097         }
41098     },
41099     
41100     getRawValue : function()
41101     {
41102         var v = this.inputEl().getValue();
41103         
41104         return v;
41105     },
41106     
41107     getValue : function()
41108     {
41109         return this.fixPrecision(this.parseValue(this.getRawValue()));
41110     },
41111     
41112     parseValue : function(value)
41113     {
41114         if(this.thousandsDelimiter) {
41115             value += "";
41116             r = new RegExp(",", "g");
41117             value = value.replace(r, "");
41118         }
41119         
41120         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41121         return isNaN(value) ? '' : value;
41122         
41123     },
41124     
41125     fixPrecision : function(value)
41126     {
41127         if(this.thousandsDelimiter) {
41128             value += "";
41129             r = new RegExp(",", "g");
41130             value = value.replace(r, "");
41131         }
41132         
41133         var nan = isNaN(value);
41134         
41135         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41136             return nan ? '' : value;
41137         }
41138         return parseFloat(value).toFixed(this.decimalPrecision);
41139     },
41140     
41141     decimalPrecisionFcn : function(v)
41142     {
41143         return Math.floor(v);
41144     },
41145     
41146     validateValue : function(value)
41147     {
41148         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41149             return false;
41150         }
41151         
41152         var num = this.parseValue(value);
41153         
41154         if(isNaN(num)){
41155             this.markInvalid(String.format(this.nanText, value));
41156             return false;
41157         }
41158         
41159         if(num < this.minValue){
41160             this.markInvalid(String.format(this.minText, this.minValue));
41161             return false;
41162         }
41163         
41164         if(num > this.maxValue){
41165             this.markInvalid(String.format(this.maxText, this.maxValue));
41166             return false;
41167         }
41168         
41169         return true;
41170     },
41171     
41172     validate : function()
41173     {
41174         if(this.disabled || this.allowBlank){
41175             this.markValid();
41176             return true;
41177         }
41178         
41179         var currency = this.getCurrency();
41180         
41181         if(this.validateValue(this.getRawValue()) && currency.length){
41182             this.markValid();
41183             return true;
41184         }
41185         
41186         this.markInvalid();
41187         return false;
41188     },
41189     
41190     getName: function()
41191     {
41192         return this.name;
41193     },
41194     
41195     beforeBlur : function()
41196     {
41197         if(!this.castInt){
41198             return;
41199         }
41200         
41201         var v = this.parseValue(this.getRawValue());
41202         
41203         if(v || v == 0){
41204             this.setValue(v);
41205         }
41206     },
41207     
41208     onBlur : function()
41209     {
41210         this.beforeBlur();
41211         
41212         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41213             //this.el.removeClass(this.focusClass);
41214         }
41215         
41216         this.hasFocus = false;
41217         
41218         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41219             this.validate();
41220         }
41221         
41222         var v = this.getValue();
41223         
41224         if(String(v) !== String(this.startValue)){
41225             this.fireEvent('change', this, v, this.startValue);
41226         }
41227         
41228         this.fireEvent("blur", this);
41229     },
41230     
41231     inputEl : function()
41232     {
41233         return this.el.select('.roo-money-amount-input', true).first();
41234     },
41235     
41236     currencyEl : function()
41237     {
41238         return this.el.select('.roo-money-currency-input', true).first();
41239     },
41240     
41241     hiddenEl : function()
41242     {
41243         return this.el.select('input.hidden-number-input',true).first();
41244     }
41245     
41246 });