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                this.el.select('.navbar-collapse',true).toggleClass('in collapse');                                 
3865             }
3866             
3867         }, this);
3868         
3869         var mark = {
3870             tag: "div",
3871             cls:"x-dlg-mask"
3872         };
3873         
3874         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3875         
3876         var size = this.el.getSize();
3877         this.maskEl.setSize(size.width, size.height);
3878         this.maskEl.enableDisplayMode("block");
3879         this.maskEl.hide();
3880         
3881         if(this.loadMask){
3882             this.maskEl.show();
3883         }
3884     },
3885     
3886     
3887     getChildContainer : function()
3888     {
3889         if (this.el.select('.collapse').getCount()) {
3890             return this.el.select('.collapse',true).first();
3891         }
3892         
3893         return this.el;
3894     },
3895     
3896     mask : function()
3897     {
3898         this.maskEl.show();
3899     },
3900     
3901     unmask : function()
3902     {
3903         this.maskEl.hide();
3904     } 
3905     
3906     
3907     
3908     
3909 });
3910
3911
3912
3913  
3914
3915  /*
3916  * - LGPL
3917  *
3918  * navbar
3919  * 
3920  */
3921
3922 /**
3923  * @class Roo.bootstrap.NavSimplebar
3924  * @extends Roo.bootstrap.Navbar
3925  * Bootstrap Sidebar class
3926  *
3927  * @cfg {Boolean} inverse is inverted color
3928  * 
3929  * @cfg {String} type (nav | pills | tabs)
3930  * @cfg {Boolean} arrangement stacked | justified
3931  * @cfg {String} align (left | right) alignment
3932  * 
3933  * @cfg {Boolean} main (true|false) main nav bar? default false
3934  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3935  * 
3936  * @cfg {String} tag (header|footer|nav|div) default is nav 
3937
3938  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3939  * 
3940  * 
3941  * @constructor
3942  * Create a new Sidebar
3943  * @param {Object} config The config object
3944  */
3945
3946
3947 Roo.bootstrap.NavSimplebar = function(config){
3948     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3949 };
3950
3951 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3952     
3953     inverse: false,
3954     
3955     type: false,
3956     arrangement: '',
3957     align : false,
3958     
3959     weight : 'light',
3960     
3961     main : false,
3962     
3963     
3964     tag : false,
3965     
3966     
3967     getAutoCreate : function(){
3968         
3969         
3970         var cfg = {
3971             tag : this.tag || 'div',
3972             cls : 'navbar navbar-expand-lg'
3973         };
3974         if (['light','white'].indexOf(this.weight) > -1) {
3975             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3976         }
3977         cfg.cls += ' bg-' + this.weight;
3978         
3979           
3980         
3981         cfg.cn = [
3982             {
3983                 cls: 'nav',
3984                 tag : 'ul'
3985             }
3986         ];
3987         
3988          
3989         this.type = this.type || 'nav';
3990         if (['tabs','pills'].indexOf(this.type)!==-1) {
3991             cfg.cn[0].cls += ' nav-' + this.type
3992         
3993         
3994         } else {
3995             if (this.type!=='nav') {
3996                 Roo.log('nav type must be nav/tabs/pills')
3997             }
3998             cfg.cn[0].cls += ' navbar-nav'
3999         }
4000         
4001         
4002         
4003         
4004         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4005             cfg.cn[0].cls += ' nav-' + this.arrangement;
4006         }
4007         
4008         
4009         if (this.align === 'right') {
4010             cfg.cn[0].cls += ' navbar-right';
4011         }
4012         
4013         if (this.inverse) {
4014             cfg.cls += ' navbar-inverse';
4015             
4016         }
4017         
4018         
4019         return cfg;
4020     
4021         
4022     }
4023     
4024     
4025     
4026 });
4027
4028
4029
4030  
4031
4032  
4033        /*
4034  * - LGPL
4035  *
4036  * navbar
4037  * navbar-fixed-top
4038  * navbar-expand-md  fixed-top 
4039  */
4040
4041 /**
4042  * @class Roo.bootstrap.NavHeaderbar
4043  * @extends Roo.bootstrap.NavSimplebar
4044  * Bootstrap Sidebar class
4045  *
4046  * @cfg {String} brand what is brand
4047  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4048  * @cfg {String} brand_href href of the brand
4049  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4050  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4051  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4052  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4053  * 
4054  * @constructor
4055  * Create a new Sidebar
4056  * @param {Object} config The config object
4057  */
4058
4059
4060 Roo.bootstrap.NavHeaderbar = function(config){
4061     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4062       
4063 };
4064
4065 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4066     
4067     position: '',
4068     brand: '',
4069     brand_href: false,
4070     srButton : true,
4071     autohide : false,
4072     desktopCenter : false,
4073    
4074     
4075     getAutoCreate : function(){
4076         
4077         var   cfg = {
4078             tag: this.nav || 'nav',
4079             cls: 'navbar navbar-expand-md',
4080             role: 'navigation',
4081             cn: []
4082         };
4083         
4084         var cn = cfg.cn;
4085         if (this.desktopCenter) {
4086             cn.push({cls : 'container', cn : []});
4087             cn = cn[0].cn;
4088         }
4089         
4090         if(this.srButton){
4091             var btn = {
4092                 tag: 'button',
4093                 type: 'button',
4094                 cls: 'navbar-toggle navbar-toggler',
4095                 'data-toggle': 'collapse',
4096                 cn: [
4097                     {
4098                         tag: 'span',
4099                         cls: 'sr-only',
4100                         html: 'Toggle navigation'
4101                     },
4102                     {
4103                         tag: 'span',
4104                         cls: 'icon-bar navbar-toggler-icon'
4105                     },
4106                     {
4107                         tag: 'span',
4108                         cls: 'icon-bar'
4109                     },
4110                     {
4111                         tag: 'span',
4112                         cls: 'icon-bar'
4113                     }
4114                 ]
4115             };
4116             
4117             cn.push( Roo.bootstrap.version == 4 ? btn : {
4118                 tag: 'div',
4119                 cls: 'navbar-header',
4120                 cn: [
4121                     btn
4122                 ]
4123             });
4124         }
4125         
4126         cn.push({
4127             tag: 'div',
4128             cls: 'collapse navbar-collapse',
4129             cn : []
4130         });
4131         
4132         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4133         
4134         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4135             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4136             
4137             // tag can override this..
4138             
4139             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4140         }
4141         
4142         if (this.brand !== '') {
4143             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4144             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4145                 tag: 'a',
4146                 href: this.brand_href ? this.brand_href : '#',
4147                 cls: 'navbar-brand',
4148                 cn: [
4149                 this.brand
4150                 ]
4151             });
4152         }
4153         
4154         if(this.main){
4155             cfg.cls += ' main-nav';
4156         }
4157         
4158         
4159         return cfg;
4160
4161         
4162     },
4163     getHeaderChildContainer : function()
4164     {
4165         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4166             return this.el.select('.navbar-header',true).first();
4167         }
4168         
4169         return this.getChildContainer();
4170     },
4171     
4172     
4173     initEvents : function()
4174     {
4175         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4176         
4177         if (this.autohide) {
4178             
4179             var prevScroll = 0;
4180             var ft = this.el;
4181             
4182             Roo.get(document).on('scroll',function(e) {
4183                 var ns = Roo.get(document).getScroll().top;
4184                 var os = prevScroll;
4185                 prevScroll = ns;
4186                 
4187                 if(ns > os){
4188                     ft.removeClass('slideDown');
4189                     ft.addClass('slideUp');
4190                     return;
4191                 }
4192                 ft.removeClass('slideUp');
4193                 ft.addClass('slideDown');
4194                  
4195               
4196           },this);
4197         }
4198     }    
4199     
4200 });
4201
4202
4203
4204  
4205
4206  /*
4207  * - LGPL
4208  *
4209  * navbar
4210  * 
4211  */
4212
4213 /**
4214  * @class Roo.bootstrap.NavSidebar
4215  * @extends Roo.bootstrap.Navbar
4216  * Bootstrap Sidebar class
4217  * 
4218  * @constructor
4219  * Create a new Sidebar
4220  * @param {Object} config The config object
4221  */
4222
4223
4224 Roo.bootstrap.NavSidebar = function(config){
4225     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4226 };
4227
4228 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4229     
4230     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4231     
4232     getAutoCreate : function(){
4233         
4234         
4235         return  {
4236             tag: 'div',
4237             cls: 'sidebar sidebar-nav'
4238         };
4239     
4240         
4241     }
4242     
4243     
4244     
4245 });
4246
4247
4248
4249  
4250
4251  /*
4252  * - LGPL
4253  *
4254  * nav group
4255  * 
4256  */
4257
4258 /**
4259  * @class Roo.bootstrap.NavGroup
4260  * @extends Roo.bootstrap.Component
4261  * Bootstrap NavGroup class
4262  * @cfg {String} align (left|right)
4263  * @cfg {Boolean} inverse
4264  * @cfg {String} type (nav|pills|tab) default nav
4265  * @cfg {String} navId - reference Id for navbar.
4266
4267  * 
4268  * @constructor
4269  * Create a new nav group
4270  * @param {Object} config The config object
4271  */
4272
4273 Roo.bootstrap.NavGroup = function(config){
4274     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4275     this.navItems = [];
4276    
4277     Roo.bootstrap.NavGroup.register(this);
4278      this.addEvents({
4279         /**
4280              * @event changed
4281              * Fires when the active item changes
4282              * @param {Roo.bootstrap.NavGroup} this
4283              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4284              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4285          */
4286         'changed': true
4287      });
4288     
4289 };
4290
4291 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4292     
4293     align: '',
4294     inverse: false,
4295     form: false,
4296     type: 'nav',
4297     navId : '',
4298     // private
4299     
4300     navItems : false, 
4301     
4302     getAutoCreate : function()
4303     {
4304         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4305         
4306         cfg = {
4307             tag : 'ul',
4308             cls: 'nav' 
4309         };
4310         
4311         if (['tabs','pills'].indexOf(this.type)!==-1) {
4312             cfg.cls += ' nav-' + this.type
4313         } else {
4314             if (this.type!=='nav') {
4315                 Roo.log('nav type must be nav/tabs/pills')
4316             }
4317             cfg.cls += ' navbar-nav'
4318         }
4319         
4320         if (this.parent() && this.parent().sidebar) {
4321             cfg = {
4322                 tag: 'ul',
4323                 cls: 'dashboard-menu sidebar-menu'
4324             };
4325             
4326             return cfg;
4327         }
4328         
4329         if (this.form === true) {
4330             cfg = {
4331                 tag: 'form',
4332                 cls: 'navbar-form'
4333             };
4334             
4335             if (this.align === 'right') {
4336                 cfg.cls += ' navbar-right ml-md-auto';
4337             } else {
4338                 cfg.cls += ' navbar-left';
4339             }
4340         }
4341         
4342         if (this.align === 'right') {
4343             cfg.cls += ' navbar-right ml-md-auto';
4344         } else {
4345             cfg.cls += ' mr-auto';
4346         }
4347         
4348         if (this.inverse) {
4349             cfg.cls += ' navbar-inverse';
4350             
4351         }
4352         
4353         
4354         return cfg;
4355     },
4356     /**
4357     * sets the active Navigation item
4358     * @param {Roo.bootstrap.NavItem} the new current navitem
4359     */
4360     setActiveItem : function(item)
4361     {
4362         var prev = false;
4363         Roo.each(this.navItems, function(v){
4364             if (v == item) {
4365                 return ;
4366             }
4367             if (v.isActive()) {
4368                 v.setActive(false, true);
4369                 prev = v;
4370                 
4371             }
4372             
4373         });
4374
4375         item.setActive(true, true);
4376         this.fireEvent('changed', this, item, prev);
4377         
4378         
4379     },
4380     /**
4381     * gets the active Navigation item
4382     * @return {Roo.bootstrap.NavItem} the current navitem
4383     */
4384     getActive : function()
4385     {
4386         
4387         var prev = false;
4388         Roo.each(this.navItems, function(v){
4389             
4390             if (v.isActive()) {
4391                 prev = v;
4392                 
4393             }
4394             
4395         });
4396         return prev;
4397     },
4398     
4399     indexOfNav : function()
4400     {
4401         
4402         var prev = false;
4403         Roo.each(this.navItems, function(v,i){
4404             
4405             if (v.isActive()) {
4406                 prev = i;
4407                 
4408             }
4409             
4410         });
4411         return prev;
4412     },
4413     /**
4414     * adds a Navigation item
4415     * @param {Roo.bootstrap.NavItem} the navitem to add
4416     */
4417     addItem : function(cfg)
4418     {
4419         var cn = new Roo.bootstrap.NavItem(cfg);
4420         this.register(cn);
4421         cn.parentId = this.id;
4422         cn.onRender(this.el, null);
4423         return cn;
4424     },
4425     /**
4426     * register a Navigation item
4427     * @param {Roo.bootstrap.NavItem} the navitem to add
4428     */
4429     register : function(item)
4430     {
4431         this.navItems.push( item);
4432         item.navId = this.navId;
4433     
4434     },
4435     
4436     /**
4437     * clear all the Navigation item
4438     */
4439    
4440     clearAll : function()
4441     {
4442         this.navItems = [];
4443         this.el.dom.innerHTML = '';
4444     },
4445     
4446     getNavItem: function(tabId)
4447     {
4448         var ret = false;
4449         Roo.each(this.navItems, function(e) {
4450             if (e.tabId == tabId) {
4451                ret =  e;
4452                return false;
4453             }
4454             return true;
4455             
4456         });
4457         return ret;
4458     },
4459     
4460     setActiveNext : function()
4461     {
4462         var i = this.indexOfNav(this.getActive());
4463         if (i > this.navItems.length) {
4464             return;
4465         }
4466         this.setActiveItem(this.navItems[i+1]);
4467     },
4468     setActivePrev : function()
4469     {
4470         var i = this.indexOfNav(this.getActive());
4471         if (i  < 1) {
4472             return;
4473         }
4474         this.setActiveItem(this.navItems[i-1]);
4475     },
4476     clearWasActive : function(except) {
4477         Roo.each(this.navItems, function(e) {
4478             if (e.tabId != except.tabId && e.was_active) {
4479                e.was_active = false;
4480                return false;
4481             }
4482             return true;
4483             
4484         });
4485     },
4486     getWasActive : function ()
4487     {
4488         var r = false;
4489         Roo.each(this.navItems, function(e) {
4490             if (e.was_active) {
4491                r = e;
4492                return false;
4493             }
4494             return true;
4495             
4496         });
4497         return r;
4498     }
4499     
4500     
4501 });
4502
4503  
4504 Roo.apply(Roo.bootstrap.NavGroup, {
4505     
4506     groups: {},
4507      /**
4508     * register a Navigation Group
4509     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4510     */
4511     register : function(navgrp)
4512     {
4513         this.groups[navgrp.navId] = navgrp;
4514         
4515     },
4516     /**
4517     * fetch a Navigation Group based on the navigation ID
4518     * @param {string} the navgroup to add
4519     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4520     */
4521     get: function(navId) {
4522         if (typeof(this.groups[navId]) == 'undefined') {
4523             return false;
4524             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4525         }
4526         return this.groups[navId] ;
4527     }
4528     
4529     
4530     
4531 });
4532
4533  /*
4534  * - LGPL
4535  *
4536  * row
4537  * 
4538  */
4539
4540 /**
4541  * @class Roo.bootstrap.NavItem
4542  * @extends Roo.bootstrap.Component
4543  * Bootstrap Navbar.NavItem class
4544  * @cfg {String} href  link to
4545  * @cfg {String} html content of button
4546  * @cfg {String} badge text inside badge
4547  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4548  * @cfg {String} glyphicon DEPRICATED - use fa
4549  * @cfg {String} icon DEPRICATED - use fa
4550  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4551  * @cfg {Boolean} active Is item active
4552  * @cfg {Boolean} disabled Is item disabled
4553  
4554  * @cfg {Boolean} preventDefault (true | false) default false
4555  * @cfg {String} tabId the tab that this item activates.
4556  * @cfg {String} tagtype (a|span) render as a href or span?
4557  * @cfg {Boolean} animateRef (true|false) link to element default false  
4558   
4559  * @constructor
4560  * Create a new Navbar Item
4561  * @param {Object} config The config object
4562  */
4563 Roo.bootstrap.NavItem = function(config){
4564     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4565     this.addEvents({
4566         // raw events
4567         /**
4568          * @event click
4569          * The raw click event for the entire grid.
4570          * @param {Roo.EventObject} e
4571          */
4572         "click" : true,
4573          /**
4574             * @event changed
4575             * Fires when the active item active state changes
4576             * @param {Roo.bootstrap.NavItem} this
4577             * @param {boolean} state the new state
4578              
4579          */
4580         'changed': true,
4581         /**
4582             * @event scrollto
4583             * Fires when scroll to element
4584             * @param {Roo.bootstrap.NavItem} this
4585             * @param {Object} options
4586             * @param {Roo.EventObject} e
4587              
4588          */
4589         'scrollto': true
4590     });
4591    
4592 };
4593
4594 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4595     
4596     href: false,
4597     html: '',
4598     badge: '',
4599     icon: false,
4600     fa : false,
4601     glyphicon: false,
4602     active: false,
4603     preventDefault : false,
4604     tabId : false,
4605     tagtype : 'a',
4606     disabled : false,
4607     animateRef : false,
4608     was_active : false,
4609     
4610     getAutoCreate : function(){
4611          
4612         var cfg = {
4613             tag: 'li',
4614             cls: 'nav-item'
4615             
4616         };
4617         
4618         if (this.active) {
4619             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4620         }
4621         if (this.disabled) {
4622             cfg.cls += ' disabled';
4623         }
4624         
4625         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4626             cfg.cn = [
4627                 {
4628                     tag: this.tagtype,
4629                     href : this.href || "#",
4630                     html: this.html || ''
4631                 }
4632             ];
4633             if (this.tagtype == 'a') {
4634                 cfg.cn[0].cls = 'nav-link';
4635             }
4636             if (this.icon) {
4637                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4638             }
4639             if (this.fa) {
4640                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4641             }
4642             if(this.glyphicon) {
4643                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4644             }
4645             
4646             if (this.menu) {
4647                 
4648                 cfg.cn[0].html += " <span class='caret'></span>";
4649              
4650             }
4651             
4652             if (this.badge !== '') {
4653                  
4654                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4655             }
4656         }
4657         
4658         
4659         
4660         return cfg;
4661     },
4662     initEvents: function() 
4663     {
4664         if (typeof (this.menu) != 'undefined') {
4665             this.menu.parentType = this.xtype;
4666             this.menu.triggerEl = this.el;
4667             this.menu = this.addxtype(Roo.apply({}, this.menu));
4668         }
4669         
4670         this.el.select('a',true).on('click', this.onClick, this);
4671         
4672         if(this.tagtype == 'span'){
4673             this.el.select('span',true).on('click', this.onClick, this);
4674         }
4675        
4676         // at this point parent should be available..
4677         this.parent().register(this);
4678     },
4679     
4680     onClick : function(e)
4681     {
4682         if (e.getTarget('.dropdown-menu-item')) {
4683             // did you click on a menu itemm.... - then don't trigger onclick..
4684             return;
4685         }
4686         
4687         if(
4688                 this.preventDefault || 
4689                 this.href == '#' 
4690         ){
4691             Roo.log("NavItem - prevent Default?");
4692             e.preventDefault();
4693         }
4694         
4695         if (this.disabled) {
4696             return;
4697         }
4698         
4699         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4700         if (tg && tg.transition) {
4701             Roo.log("waiting for the transitionend");
4702             return;
4703         }
4704         
4705         
4706         
4707         //Roo.log("fire event clicked");
4708         if(this.fireEvent('click', this, e) === false){
4709             return;
4710         };
4711         
4712         if(this.tagtype == 'span'){
4713             return;
4714         }
4715         
4716         //Roo.log(this.href);
4717         var ael = this.el.select('a',true).first();
4718         //Roo.log(ael);
4719         
4720         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4721             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4722             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4723                 return; // ignore... - it's a 'hash' to another page.
4724             }
4725             Roo.log("NavItem - prevent Default?");
4726             e.preventDefault();
4727             this.scrollToElement(e);
4728         }
4729         
4730         
4731         var p =  this.parent();
4732    
4733         if (['tabs','pills'].indexOf(p.type)!==-1) {
4734             if (typeof(p.setActiveItem) !== 'undefined') {
4735                 p.setActiveItem(this);
4736             }
4737         }
4738         
4739         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4740         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4741             // remove the collapsed menu expand...
4742             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4743         }
4744     },
4745     
4746     isActive: function () {
4747         return this.active
4748     },
4749     setActive : function(state, fire, is_was_active)
4750     {
4751         if (this.active && !state && this.navId) {
4752             this.was_active = true;
4753             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4754             if (nv) {
4755                 nv.clearWasActive(this);
4756             }
4757             
4758         }
4759         this.active = state;
4760         
4761         if (!state ) {
4762             this.el.removeClass('active');
4763         } else if (!this.el.hasClass('active')) {
4764             this.el.addClass('active');
4765         }
4766         if (fire) {
4767             this.fireEvent('changed', this, state);
4768         }
4769         
4770         // show a panel if it's registered and related..
4771         
4772         if (!this.navId || !this.tabId || !state || is_was_active) {
4773             return;
4774         }
4775         
4776         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4777         if (!tg) {
4778             return;
4779         }
4780         var pan = tg.getPanelByName(this.tabId);
4781         if (!pan) {
4782             return;
4783         }
4784         // if we can not flip to new panel - go back to old nav highlight..
4785         if (false == tg.showPanel(pan)) {
4786             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4787             if (nv) {
4788                 var onav = nv.getWasActive();
4789                 if (onav) {
4790                     onav.setActive(true, false, true);
4791                 }
4792             }
4793             
4794         }
4795         
4796         
4797         
4798     },
4799      // this should not be here...
4800     setDisabled : function(state)
4801     {
4802         this.disabled = state;
4803         if (!state ) {
4804             this.el.removeClass('disabled');
4805         } else if (!this.el.hasClass('disabled')) {
4806             this.el.addClass('disabled');
4807         }
4808         
4809     },
4810     
4811     /**
4812      * Fetch the element to display the tooltip on.
4813      * @return {Roo.Element} defaults to this.el
4814      */
4815     tooltipEl : function()
4816     {
4817         return this.el.select('' + this.tagtype + '', true).first();
4818     },
4819     
4820     scrollToElement : function(e)
4821     {
4822         var c = document.body;
4823         
4824         /*
4825          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4826          */
4827         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4828             c = document.documentElement;
4829         }
4830         
4831         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4832         
4833         if(!target){
4834             return;
4835         }
4836
4837         var o = target.calcOffsetsTo(c);
4838         
4839         var options = {
4840             target : target,
4841             value : o[1]
4842         };
4843         
4844         this.fireEvent('scrollto', this, options, e);
4845         
4846         Roo.get(c).scrollTo('top', options.value, true);
4847         
4848         return;
4849     }
4850 });
4851  
4852
4853  /*
4854  * - LGPL
4855  *
4856  * sidebar item
4857  *
4858  *  li
4859  *    <span> icon </span>
4860  *    <span> text </span>
4861  *    <span>badge </span>
4862  */
4863
4864 /**
4865  * @class Roo.bootstrap.NavSidebarItem
4866  * @extends Roo.bootstrap.NavItem
4867  * Bootstrap Navbar.NavSidebarItem class
4868  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4869  * {Boolean} open is the menu open
4870  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4871  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4872  * {String} buttonSize (sm|md|lg)the extra classes for the button
4873  * {Boolean} showArrow show arrow next to the text (default true)
4874  * @constructor
4875  * Create a new Navbar Button
4876  * @param {Object} config The config object
4877  */
4878 Roo.bootstrap.NavSidebarItem = function(config){
4879     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4880     this.addEvents({
4881         // raw events
4882         /**
4883          * @event click
4884          * The raw click event for the entire grid.
4885          * @param {Roo.EventObject} e
4886          */
4887         "click" : true,
4888          /**
4889             * @event changed
4890             * Fires when the active item active state changes
4891             * @param {Roo.bootstrap.NavSidebarItem} this
4892             * @param {boolean} state the new state
4893              
4894          */
4895         'changed': true
4896     });
4897    
4898 };
4899
4900 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4901     
4902     badgeWeight : 'default',
4903     
4904     open: false,
4905     
4906     buttonView : false,
4907     
4908     buttonWeight : 'default',
4909     
4910     buttonSize : 'md',
4911     
4912     showArrow : true,
4913     
4914     getAutoCreate : function(){
4915         
4916         
4917         var a = {
4918                 tag: 'a',
4919                 href : this.href || '#',
4920                 cls: '',
4921                 html : '',
4922                 cn : []
4923         };
4924         
4925         if(this.buttonView){
4926             a = {
4927                 tag: 'button',
4928                 href : this.href || '#',
4929                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4930                 html : this.html,
4931                 cn : []
4932             };
4933         }
4934         
4935         var cfg = {
4936             tag: 'li',
4937             cls: '',
4938             cn: [ a ]
4939         };
4940         
4941         if (this.active) {
4942             cfg.cls += ' active';
4943         }
4944         
4945         if (this.disabled) {
4946             cfg.cls += ' disabled';
4947         }
4948         if (this.open) {
4949             cfg.cls += ' open x-open';
4950         }
4951         // left icon..
4952         if (this.glyphicon || this.icon) {
4953             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4954             a.cn.push({ tag : 'i', cls : c }) ;
4955         }
4956         
4957         if(!this.buttonView){
4958             var span = {
4959                 tag: 'span',
4960                 html : this.html || ''
4961             };
4962
4963             a.cn.push(span);
4964             
4965         }
4966         
4967         if (this.badge !== '') {
4968             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4969         }
4970         
4971         if (this.menu) {
4972             
4973             if(this.showArrow){
4974                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4975             }
4976             
4977             a.cls += ' dropdown-toggle treeview' ;
4978         }
4979         
4980         return cfg;
4981     },
4982     
4983     initEvents : function()
4984     { 
4985         if (typeof (this.menu) != 'undefined') {
4986             this.menu.parentType = this.xtype;
4987             this.menu.triggerEl = this.el;
4988             this.menu = this.addxtype(Roo.apply({}, this.menu));
4989         }
4990         
4991         this.el.on('click', this.onClick, this);
4992         
4993         if(this.badge !== ''){
4994             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4995         }
4996         
4997     },
4998     
4999     onClick : function(e)
5000     {
5001         if(this.disabled){
5002             e.preventDefault();
5003             return;
5004         }
5005         
5006         if(this.preventDefault){
5007             e.preventDefault();
5008         }
5009         
5010         this.fireEvent('click', this);
5011     },
5012     
5013     disable : function()
5014     {
5015         this.setDisabled(true);
5016     },
5017     
5018     enable : function()
5019     {
5020         this.setDisabled(false);
5021     },
5022     
5023     setDisabled : function(state)
5024     {
5025         if(this.disabled == state){
5026             return;
5027         }
5028         
5029         this.disabled = state;
5030         
5031         if (state) {
5032             this.el.addClass('disabled');
5033             return;
5034         }
5035         
5036         this.el.removeClass('disabled');
5037         
5038         return;
5039     },
5040     
5041     setActive : function(state)
5042     {
5043         if(this.active == state){
5044             return;
5045         }
5046         
5047         this.active = state;
5048         
5049         if (state) {
5050             this.el.addClass('active');
5051             return;
5052         }
5053         
5054         this.el.removeClass('active');
5055         
5056         return;
5057     },
5058     
5059     isActive: function () 
5060     {
5061         return this.active;
5062     },
5063     
5064     setBadge : function(str)
5065     {
5066         if(!this.badgeEl){
5067             return;
5068         }
5069         
5070         this.badgeEl.dom.innerHTML = str;
5071     }
5072     
5073    
5074      
5075  
5076 });
5077  
5078
5079  /*
5080  * - LGPL
5081  *
5082  * row
5083  * 
5084  */
5085
5086 /**
5087  * @class Roo.bootstrap.Row
5088  * @extends Roo.bootstrap.Component
5089  * Bootstrap Row class (contains columns...)
5090  * 
5091  * @constructor
5092  * Create a new Row
5093  * @param {Object} config The config object
5094  */
5095
5096 Roo.bootstrap.Row = function(config){
5097     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5098 };
5099
5100 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5101     
5102     getAutoCreate : function(){
5103        return {
5104             cls: 'row clearfix'
5105        };
5106     }
5107     
5108     
5109 });
5110
5111  
5112
5113  /*
5114  * - LGPL
5115  *
5116  * element
5117  * 
5118  */
5119
5120 /**
5121  * @class Roo.bootstrap.Element
5122  * @extends Roo.bootstrap.Component
5123  * Bootstrap Element class
5124  * @cfg {String} html contents of the element
5125  * @cfg {String} tag tag of the element
5126  * @cfg {String} cls class of the element
5127  * @cfg {Boolean} preventDefault (true|false) default false
5128  * @cfg {Boolean} clickable (true|false) default false
5129  * 
5130  * @constructor
5131  * Create a new Element
5132  * @param {Object} config The config object
5133  */
5134
5135 Roo.bootstrap.Element = function(config){
5136     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5137     
5138     this.addEvents({
5139         // raw events
5140         /**
5141          * @event click
5142          * When a element is chick
5143          * @param {Roo.bootstrap.Element} this
5144          * @param {Roo.EventObject} e
5145          */
5146         "click" : true
5147     });
5148 };
5149
5150 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5151     
5152     tag: 'div',
5153     cls: '',
5154     html: '',
5155     preventDefault: false, 
5156     clickable: false,
5157     
5158     getAutoCreate : function(){
5159         
5160         var cfg = {
5161             tag: this.tag,
5162             // cls: this.cls, double assign in parent class Component.js :: onRender
5163             html: this.html
5164         };
5165         
5166         return cfg;
5167     },
5168     
5169     initEvents: function() 
5170     {
5171         Roo.bootstrap.Element.superclass.initEvents.call(this);
5172         
5173         if(this.clickable){
5174             this.el.on('click', this.onClick, this);
5175         }
5176         
5177     },
5178     
5179     onClick : function(e)
5180     {
5181         if(this.preventDefault){
5182             e.preventDefault();
5183         }
5184         
5185         this.fireEvent('click', this, e);
5186     },
5187     
5188     getValue : function()
5189     {
5190         return this.el.dom.innerHTML;
5191     },
5192     
5193     setValue : function(value)
5194     {
5195         this.el.dom.innerHTML = value;
5196     }
5197    
5198 });
5199
5200  
5201
5202  /*
5203  * - LGPL
5204  *
5205  * pagination
5206  * 
5207  */
5208
5209 /**
5210  * @class Roo.bootstrap.Pagination
5211  * @extends Roo.bootstrap.Component
5212  * Bootstrap Pagination class
5213  * @cfg {String} size xs | sm | md | lg
5214  * @cfg {Boolean} inverse false | true
5215  * 
5216  * @constructor
5217  * Create a new Pagination
5218  * @param {Object} config The config object
5219  */
5220
5221 Roo.bootstrap.Pagination = function(config){
5222     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5223 };
5224
5225 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5226     
5227     cls: false,
5228     size: false,
5229     inverse: false,
5230     
5231     getAutoCreate : function(){
5232         var cfg = {
5233             tag: 'ul',
5234                 cls: 'pagination'
5235         };
5236         if (this.inverse) {
5237             cfg.cls += ' inverse';
5238         }
5239         if (this.html) {
5240             cfg.html=this.html;
5241         }
5242         if (this.cls) {
5243             cfg.cls += " " + this.cls;
5244         }
5245         return cfg;
5246     }
5247    
5248 });
5249
5250  
5251
5252  /*
5253  * - LGPL
5254  *
5255  * Pagination item
5256  * 
5257  */
5258
5259
5260 /**
5261  * @class Roo.bootstrap.PaginationItem
5262  * @extends Roo.bootstrap.Component
5263  * Bootstrap PaginationItem class
5264  * @cfg {String} html text
5265  * @cfg {String} href the link
5266  * @cfg {Boolean} preventDefault (true | false) default true
5267  * @cfg {Boolean} active (true | false) default false
5268  * @cfg {Boolean} disabled default false
5269  * 
5270  * 
5271  * @constructor
5272  * Create a new PaginationItem
5273  * @param {Object} config The config object
5274  */
5275
5276
5277 Roo.bootstrap.PaginationItem = function(config){
5278     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5279     this.addEvents({
5280         // raw events
5281         /**
5282          * @event click
5283          * The raw click event for the entire grid.
5284          * @param {Roo.EventObject} e
5285          */
5286         "click" : true
5287     });
5288 };
5289
5290 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5291     
5292     href : false,
5293     html : false,
5294     preventDefault: true,
5295     active : false,
5296     cls : false,
5297     disabled: false,
5298     
5299     getAutoCreate : function(){
5300         var cfg= {
5301             tag: 'li',
5302             cn: [
5303                 {
5304                     tag : 'a',
5305                     href : this.href ? this.href : '#',
5306                     html : this.html ? this.html : ''
5307                 }
5308             ]
5309         };
5310         
5311         if(this.cls){
5312             cfg.cls = this.cls;
5313         }
5314         
5315         if(this.disabled){
5316             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5317         }
5318         
5319         if(this.active){
5320             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5321         }
5322         
5323         return cfg;
5324     },
5325     
5326     initEvents: function() {
5327         
5328         this.el.on('click', this.onClick, this);
5329         
5330     },
5331     onClick : function(e)
5332     {
5333         Roo.log('PaginationItem on click ');
5334         if(this.preventDefault){
5335             e.preventDefault();
5336         }
5337         
5338         if(this.disabled){
5339             return;
5340         }
5341         
5342         this.fireEvent('click', this, e);
5343     }
5344    
5345 });
5346
5347  
5348
5349  /*
5350  * - LGPL
5351  *
5352  * slider
5353  * 
5354  */
5355
5356
5357 /**
5358  * @class Roo.bootstrap.Slider
5359  * @extends Roo.bootstrap.Component
5360  * Bootstrap Slider class
5361  *    
5362  * @constructor
5363  * Create a new Slider
5364  * @param {Object} config The config object
5365  */
5366
5367 Roo.bootstrap.Slider = function(config){
5368     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5369 };
5370
5371 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5372     
5373     getAutoCreate : function(){
5374         
5375         var cfg = {
5376             tag: 'div',
5377             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5378             cn: [
5379                 {
5380                     tag: 'a',
5381                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5382                 }
5383             ]
5384         };
5385         
5386         return cfg;
5387     }
5388    
5389 });
5390
5391  /*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401  
5402
5403 /**
5404  * @class Roo.grid.ColumnModel
5405  * @extends Roo.util.Observable
5406  * This is the default implementation of a ColumnModel used by the Grid. It defines
5407  * the columns in the grid.
5408  * <br>Usage:<br>
5409  <pre><code>
5410  var colModel = new Roo.grid.ColumnModel([
5411         {header: "Ticker", width: 60, sortable: true, locked: true},
5412         {header: "Company Name", width: 150, sortable: true},
5413         {header: "Market Cap.", width: 100, sortable: true},
5414         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5415         {header: "Employees", width: 100, sortable: true, resizable: false}
5416  ]);
5417  </code></pre>
5418  * <p>
5419  
5420  * The config options listed for this class are options which may appear in each
5421  * individual column definition.
5422  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5423  * @constructor
5424  * @param {Object} config An Array of column config objects. See this class's
5425  * config objects for details.
5426 */
5427 Roo.grid.ColumnModel = function(config){
5428         /**
5429      * The config passed into the constructor
5430      */
5431     this.config = config;
5432     this.lookup = {};
5433
5434     // if no id, create one
5435     // if the column does not have a dataIndex mapping,
5436     // map it to the order it is in the config
5437     for(var i = 0, len = config.length; i < len; i++){
5438         var c = config[i];
5439         if(typeof c.dataIndex == "undefined"){
5440             c.dataIndex = i;
5441         }
5442         if(typeof c.renderer == "string"){
5443             c.renderer = Roo.util.Format[c.renderer];
5444         }
5445         if(typeof c.id == "undefined"){
5446             c.id = Roo.id();
5447         }
5448         if(c.editor && c.editor.xtype){
5449             c.editor  = Roo.factory(c.editor, Roo.grid);
5450         }
5451         if(c.editor && c.editor.isFormField){
5452             c.editor = new Roo.grid.GridEditor(c.editor);
5453         }
5454         this.lookup[c.id] = c;
5455     }
5456
5457     /**
5458      * The width of columns which have no width specified (defaults to 100)
5459      * @type Number
5460      */
5461     this.defaultWidth = 100;
5462
5463     /**
5464      * Default sortable of columns which have no sortable specified (defaults to false)
5465      * @type Boolean
5466      */
5467     this.defaultSortable = false;
5468
5469     this.addEvents({
5470         /**
5471              * @event widthchange
5472              * Fires when the width of a column changes.
5473              * @param {ColumnModel} this
5474              * @param {Number} columnIndex The column index
5475              * @param {Number} newWidth The new width
5476              */
5477             "widthchange": true,
5478         /**
5479              * @event headerchange
5480              * Fires when the text of a header changes.
5481              * @param {ColumnModel} this
5482              * @param {Number} columnIndex The column index
5483              * @param {Number} newText The new header text
5484              */
5485             "headerchange": true,
5486         /**
5487              * @event hiddenchange
5488              * Fires when a column is hidden or "unhidden".
5489              * @param {ColumnModel} this
5490              * @param {Number} columnIndex The column index
5491              * @param {Boolean} hidden true if hidden, false otherwise
5492              */
5493             "hiddenchange": true,
5494             /**
5495          * @event columnmoved
5496          * Fires when a column is moved.
5497          * @param {ColumnModel} this
5498          * @param {Number} oldIndex
5499          * @param {Number} newIndex
5500          */
5501         "columnmoved" : true,
5502         /**
5503          * @event columlockchange
5504          * Fires when a column's locked state is changed
5505          * @param {ColumnModel} this
5506          * @param {Number} colIndex
5507          * @param {Boolean} locked true if locked
5508          */
5509         "columnlockchange" : true
5510     });
5511     Roo.grid.ColumnModel.superclass.constructor.call(this);
5512 };
5513 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5514     /**
5515      * @cfg {String} header The header text to display in the Grid view.
5516      */
5517     /**
5518      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5519      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5520      * specified, the column's index is used as an index into the Record's data Array.
5521      */
5522     /**
5523      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5524      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5525      */
5526     /**
5527      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5528      * Defaults to the value of the {@link #defaultSortable} property.
5529      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5530      */
5531     /**
5532      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5533      */
5534     /**
5535      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5536      */
5537     /**
5538      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5539      */
5540     /**
5541      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5542      */
5543     /**
5544      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5545      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5546      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5547      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5548      */
5549        /**
5550      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5551      */
5552     /**
5553      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5554      */
5555     /**
5556      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5557      */
5558     /**
5559      * @cfg {String} cursor (Optional)
5560      */
5561     /**
5562      * @cfg {String} tooltip (Optional)
5563      */
5564     /**
5565      * @cfg {Number} xs (Optional)
5566      */
5567     /**
5568      * @cfg {Number} sm (Optional)
5569      */
5570     /**
5571      * @cfg {Number} md (Optional)
5572      */
5573     /**
5574      * @cfg {Number} lg (Optional)
5575      */
5576     /**
5577      * Returns the id of the column at the specified index.
5578      * @param {Number} index The column index
5579      * @return {String} the id
5580      */
5581     getColumnId : function(index){
5582         return this.config[index].id;
5583     },
5584
5585     /**
5586      * Returns the column for a specified id.
5587      * @param {String} id The column id
5588      * @return {Object} the column
5589      */
5590     getColumnById : function(id){
5591         return this.lookup[id];
5592     },
5593
5594     
5595     /**
5596      * Returns the column for a specified dataIndex.
5597      * @param {String} dataIndex The column dataIndex
5598      * @return {Object|Boolean} the column or false if not found
5599      */
5600     getColumnByDataIndex: function(dataIndex){
5601         var index = this.findColumnIndex(dataIndex);
5602         return index > -1 ? this.config[index] : false;
5603     },
5604     
5605     /**
5606      * Returns the index for a specified column id.
5607      * @param {String} id The column id
5608      * @return {Number} the index, or -1 if not found
5609      */
5610     getIndexById : function(id){
5611         for(var i = 0, len = this.config.length; i < len; i++){
5612             if(this.config[i].id == id){
5613                 return i;
5614             }
5615         }
5616         return -1;
5617     },
5618     
5619     /**
5620      * Returns the index for a specified column dataIndex.
5621      * @param {String} dataIndex The column dataIndex
5622      * @return {Number} the index, or -1 if not found
5623      */
5624     
5625     findColumnIndex : function(dataIndex){
5626         for(var i = 0, len = this.config.length; i < len; i++){
5627             if(this.config[i].dataIndex == dataIndex){
5628                 return i;
5629             }
5630         }
5631         return -1;
5632     },
5633     
5634     
5635     moveColumn : function(oldIndex, newIndex){
5636         var c = this.config[oldIndex];
5637         this.config.splice(oldIndex, 1);
5638         this.config.splice(newIndex, 0, c);
5639         this.dataMap = null;
5640         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5641     },
5642
5643     isLocked : function(colIndex){
5644         return this.config[colIndex].locked === true;
5645     },
5646
5647     setLocked : function(colIndex, value, suppressEvent){
5648         if(this.isLocked(colIndex) == value){
5649             return;
5650         }
5651         this.config[colIndex].locked = value;
5652         if(!suppressEvent){
5653             this.fireEvent("columnlockchange", this, colIndex, value);
5654         }
5655     },
5656
5657     getTotalLockedWidth : function(){
5658         var totalWidth = 0;
5659         for(var i = 0; i < this.config.length; i++){
5660             if(this.isLocked(i) && !this.isHidden(i)){
5661                 this.totalWidth += this.getColumnWidth(i);
5662             }
5663         }
5664         return totalWidth;
5665     },
5666
5667     getLockedCount : function(){
5668         for(var i = 0, len = this.config.length; i < len; i++){
5669             if(!this.isLocked(i)){
5670                 return i;
5671             }
5672         }
5673         
5674         return this.config.length;
5675     },
5676
5677     /**
5678      * Returns the number of columns.
5679      * @return {Number}
5680      */
5681     getColumnCount : function(visibleOnly){
5682         if(visibleOnly === true){
5683             var c = 0;
5684             for(var i = 0, len = this.config.length; i < len; i++){
5685                 if(!this.isHidden(i)){
5686                     c++;
5687                 }
5688             }
5689             return c;
5690         }
5691         return this.config.length;
5692     },
5693
5694     /**
5695      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5696      * @param {Function} fn
5697      * @param {Object} scope (optional)
5698      * @return {Array} result
5699      */
5700     getColumnsBy : function(fn, scope){
5701         var r = [];
5702         for(var i = 0, len = this.config.length; i < len; i++){
5703             var c = this.config[i];
5704             if(fn.call(scope||this, c, i) === true){
5705                 r[r.length] = c;
5706             }
5707         }
5708         return r;
5709     },
5710
5711     /**
5712      * Returns true if the specified column is sortable.
5713      * @param {Number} col The column index
5714      * @return {Boolean}
5715      */
5716     isSortable : function(col){
5717         if(typeof this.config[col].sortable == "undefined"){
5718             return this.defaultSortable;
5719         }
5720         return this.config[col].sortable;
5721     },
5722
5723     /**
5724      * Returns the rendering (formatting) function defined for the column.
5725      * @param {Number} col The column index.
5726      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5727      */
5728     getRenderer : function(col){
5729         if(!this.config[col].renderer){
5730             return Roo.grid.ColumnModel.defaultRenderer;
5731         }
5732         return this.config[col].renderer;
5733     },
5734
5735     /**
5736      * Sets the rendering (formatting) function for a column.
5737      * @param {Number} col The column index
5738      * @param {Function} fn The function to use to process the cell's raw data
5739      * to return HTML markup for the grid view. The render function is called with
5740      * the following parameters:<ul>
5741      * <li>Data value.</li>
5742      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5743      * <li>css A CSS style string to apply to the table cell.</li>
5744      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5745      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5746      * <li>Row index</li>
5747      * <li>Column index</li>
5748      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5749      */
5750     setRenderer : function(col, fn){
5751         this.config[col].renderer = fn;
5752     },
5753
5754     /**
5755      * Returns the width for the specified column.
5756      * @param {Number} col The column index
5757      * @return {Number}
5758      */
5759     getColumnWidth : function(col){
5760         return this.config[col].width * 1 || this.defaultWidth;
5761     },
5762
5763     /**
5764      * Sets the width for a column.
5765      * @param {Number} col The column index
5766      * @param {Number} width The new width
5767      */
5768     setColumnWidth : function(col, width, suppressEvent){
5769         this.config[col].width = width;
5770         this.totalWidth = null;
5771         if(!suppressEvent){
5772              this.fireEvent("widthchange", this, col, width);
5773         }
5774     },
5775
5776     /**
5777      * Returns the total width of all columns.
5778      * @param {Boolean} includeHidden True to include hidden column widths
5779      * @return {Number}
5780      */
5781     getTotalWidth : function(includeHidden){
5782         if(!this.totalWidth){
5783             this.totalWidth = 0;
5784             for(var i = 0, len = this.config.length; i < len; i++){
5785                 if(includeHidden || !this.isHidden(i)){
5786                     this.totalWidth += this.getColumnWidth(i);
5787                 }
5788             }
5789         }
5790         return this.totalWidth;
5791     },
5792
5793     /**
5794      * Returns the header for the specified column.
5795      * @param {Number} col The column index
5796      * @return {String}
5797      */
5798     getColumnHeader : function(col){
5799         return this.config[col].header;
5800     },
5801
5802     /**
5803      * Sets the header for a column.
5804      * @param {Number} col The column index
5805      * @param {String} header The new header
5806      */
5807     setColumnHeader : function(col, header){
5808         this.config[col].header = header;
5809         this.fireEvent("headerchange", this, col, header);
5810     },
5811
5812     /**
5813      * Returns the tooltip for the specified column.
5814      * @param {Number} col The column index
5815      * @return {String}
5816      */
5817     getColumnTooltip : function(col){
5818             return this.config[col].tooltip;
5819     },
5820     /**
5821      * Sets the tooltip for a column.
5822      * @param {Number} col The column index
5823      * @param {String} tooltip The new tooltip
5824      */
5825     setColumnTooltip : function(col, tooltip){
5826             this.config[col].tooltip = tooltip;
5827     },
5828
5829     /**
5830      * Returns the dataIndex for the specified column.
5831      * @param {Number} col The column index
5832      * @return {Number}
5833      */
5834     getDataIndex : function(col){
5835         return this.config[col].dataIndex;
5836     },
5837
5838     /**
5839      * Sets the dataIndex for a column.
5840      * @param {Number} col The column index
5841      * @param {Number} dataIndex The new dataIndex
5842      */
5843     setDataIndex : function(col, dataIndex){
5844         this.config[col].dataIndex = dataIndex;
5845     },
5846
5847     
5848     
5849     /**
5850      * Returns true if the cell is editable.
5851      * @param {Number} colIndex The column index
5852      * @param {Number} rowIndex The row index - this is nto actually used..?
5853      * @return {Boolean}
5854      */
5855     isCellEditable : function(colIndex, rowIndex){
5856         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5857     },
5858
5859     /**
5860      * Returns the editor defined for the cell/column.
5861      * return false or null to disable editing.
5862      * @param {Number} colIndex The column index
5863      * @param {Number} rowIndex The row index
5864      * @return {Object}
5865      */
5866     getCellEditor : function(colIndex, rowIndex){
5867         return this.config[colIndex].editor;
5868     },
5869
5870     /**
5871      * Sets if a column is editable.
5872      * @param {Number} col The column index
5873      * @param {Boolean} editable True if the column is editable
5874      */
5875     setEditable : function(col, editable){
5876         this.config[col].editable = editable;
5877     },
5878
5879
5880     /**
5881      * Returns true if the column is hidden.
5882      * @param {Number} colIndex The column index
5883      * @return {Boolean}
5884      */
5885     isHidden : function(colIndex){
5886         return this.config[colIndex].hidden;
5887     },
5888
5889
5890     /**
5891      * Returns true if the column width cannot be changed
5892      */
5893     isFixed : function(colIndex){
5894         return this.config[colIndex].fixed;
5895     },
5896
5897     /**
5898      * Returns true if the column can be resized
5899      * @return {Boolean}
5900      */
5901     isResizable : function(colIndex){
5902         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5903     },
5904     /**
5905      * Sets if a column is hidden.
5906      * @param {Number} colIndex The column index
5907      * @param {Boolean} hidden True if the column is hidden
5908      */
5909     setHidden : function(colIndex, hidden){
5910         this.config[colIndex].hidden = hidden;
5911         this.totalWidth = null;
5912         this.fireEvent("hiddenchange", this, colIndex, hidden);
5913     },
5914
5915     /**
5916      * Sets the editor for a column.
5917      * @param {Number} col The column index
5918      * @param {Object} editor The editor object
5919      */
5920     setEditor : function(col, editor){
5921         this.config[col].editor = editor;
5922     }
5923 });
5924
5925 Roo.grid.ColumnModel.defaultRenderer = function(value)
5926 {
5927     if(typeof value == "object") {
5928         return value;
5929     }
5930         if(typeof value == "string" && value.length < 1){
5931             return "&#160;";
5932         }
5933     
5934         return String.format("{0}", value);
5935 };
5936
5937 // Alias for backwards compatibility
5938 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5939 /*
5940  * Based on:
5941  * Ext JS Library 1.1.1
5942  * Copyright(c) 2006-2007, Ext JS, LLC.
5943  *
5944  * Originally Released Under LGPL - original licence link has changed is not relivant.
5945  *
5946  * Fork - LGPL
5947  * <script type="text/javascript">
5948  */
5949  
5950 /**
5951  * @class Roo.LoadMask
5952  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5953  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5954  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5955  * element's UpdateManager load indicator and will be destroyed after the initial load.
5956  * @constructor
5957  * Create a new LoadMask
5958  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5959  * @param {Object} config The config object
5960  */
5961 Roo.LoadMask = function(el, config){
5962     this.el = Roo.get(el);
5963     Roo.apply(this, config);
5964     if(this.store){
5965         this.store.on('beforeload', this.onBeforeLoad, this);
5966         this.store.on('load', this.onLoad, this);
5967         this.store.on('loadexception', this.onLoadException, this);
5968         this.removeMask = false;
5969     }else{
5970         var um = this.el.getUpdateManager();
5971         um.showLoadIndicator = false; // disable the default indicator
5972         um.on('beforeupdate', this.onBeforeLoad, this);
5973         um.on('update', this.onLoad, this);
5974         um.on('failure', this.onLoad, this);
5975         this.removeMask = true;
5976     }
5977 };
5978
5979 Roo.LoadMask.prototype = {
5980     /**
5981      * @cfg {Boolean} removeMask
5982      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5983      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5984      */
5985     /**
5986      * @cfg {String} msg
5987      * The text to display in a centered loading message box (defaults to 'Loading...')
5988      */
5989     msg : 'Loading...',
5990     /**
5991      * @cfg {String} msgCls
5992      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5993      */
5994     msgCls : 'x-mask-loading',
5995
5996     /**
5997      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5998      * @type Boolean
5999      */
6000     disabled: false,
6001
6002     /**
6003      * Disables the mask to prevent it from being displayed
6004      */
6005     disable : function(){
6006        this.disabled = true;
6007     },
6008
6009     /**
6010      * Enables the mask so that it can be displayed
6011      */
6012     enable : function(){
6013         this.disabled = false;
6014     },
6015     
6016     onLoadException : function()
6017     {
6018         Roo.log(arguments);
6019         
6020         if (typeof(arguments[3]) != 'undefined') {
6021             Roo.MessageBox.alert("Error loading",arguments[3]);
6022         } 
6023         /*
6024         try {
6025             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6026                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6027             }   
6028         } catch(e) {
6029             
6030         }
6031         */
6032     
6033         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6034     },
6035     // private
6036     onLoad : function()
6037     {
6038         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6039     },
6040
6041     // private
6042     onBeforeLoad : function(){
6043         if(!this.disabled){
6044             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6045         }
6046     },
6047
6048     // private
6049     destroy : function(){
6050         if(this.store){
6051             this.store.un('beforeload', this.onBeforeLoad, this);
6052             this.store.un('load', this.onLoad, this);
6053             this.store.un('loadexception', this.onLoadException, this);
6054         }else{
6055             var um = this.el.getUpdateManager();
6056             um.un('beforeupdate', this.onBeforeLoad, this);
6057             um.un('update', this.onLoad, this);
6058             um.un('failure', this.onLoad, this);
6059         }
6060     }
6061 };/*
6062  * - LGPL
6063  *
6064  * table
6065  * 
6066  */
6067
6068 /**
6069  * @class Roo.bootstrap.Table
6070  * @extends Roo.bootstrap.Component
6071  * Bootstrap Table class
6072  * @cfg {String} cls table class
6073  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6074  * @cfg {String} bgcolor Specifies the background color for a table
6075  * @cfg {Number} border Specifies whether the table cells should have borders or not
6076  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6077  * @cfg {Number} cellspacing Specifies the space between cells
6078  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6079  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6080  * @cfg {String} sortable Specifies that the table should be sortable
6081  * @cfg {String} summary Specifies a summary of the content of a table
6082  * @cfg {Number} width Specifies the width of a table
6083  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6084  * 
6085  * @cfg {boolean} striped Should the rows be alternative striped
6086  * @cfg {boolean} bordered Add borders to the table
6087  * @cfg {boolean} hover Add hover highlighting
6088  * @cfg {boolean} condensed Format condensed
6089  * @cfg {boolean} responsive Format condensed
6090  * @cfg {Boolean} loadMask (true|false) default false
6091  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6092  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6093  * @cfg {Boolean} rowSelection (true|false) default false
6094  * @cfg {Boolean} cellSelection (true|false) default false
6095  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6096  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6097  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6098  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6099  
6100  * 
6101  * @constructor
6102  * Create a new Table
6103  * @param {Object} config The config object
6104  */
6105
6106 Roo.bootstrap.Table = function(config){
6107     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6108     
6109   
6110     
6111     // BC...
6112     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6113     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6114     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6115     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6116     
6117     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6118     if (this.sm) {
6119         this.sm.grid = this;
6120         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6121         this.sm = this.selModel;
6122         this.sm.xmodule = this.xmodule || false;
6123     }
6124     
6125     if (this.cm && typeof(this.cm.config) == 'undefined') {
6126         this.colModel = new Roo.grid.ColumnModel(this.cm);
6127         this.cm = this.colModel;
6128         this.cm.xmodule = this.xmodule || false;
6129     }
6130     if (this.store) {
6131         this.store= Roo.factory(this.store, Roo.data);
6132         this.ds = this.store;
6133         this.ds.xmodule = this.xmodule || false;
6134          
6135     }
6136     if (this.footer && this.store) {
6137         this.footer.dataSource = this.ds;
6138         this.footer = Roo.factory(this.footer);
6139     }
6140     
6141     /** @private */
6142     this.addEvents({
6143         /**
6144          * @event cellclick
6145          * Fires when a cell is clicked
6146          * @param {Roo.bootstrap.Table} this
6147          * @param {Roo.Element} el
6148          * @param {Number} rowIndex
6149          * @param {Number} columnIndex
6150          * @param {Roo.EventObject} e
6151          */
6152         "cellclick" : true,
6153         /**
6154          * @event celldblclick
6155          * Fires when a cell is double clicked
6156          * @param {Roo.bootstrap.Table} this
6157          * @param {Roo.Element} el
6158          * @param {Number} rowIndex
6159          * @param {Number} columnIndex
6160          * @param {Roo.EventObject} e
6161          */
6162         "celldblclick" : true,
6163         /**
6164          * @event rowclick
6165          * Fires when a row is clicked
6166          * @param {Roo.bootstrap.Table} this
6167          * @param {Roo.Element} el
6168          * @param {Number} rowIndex
6169          * @param {Roo.EventObject} e
6170          */
6171         "rowclick" : true,
6172         /**
6173          * @event rowdblclick
6174          * Fires when a row is double clicked
6175          * @param {Roo.bootstrap.Table} this
6176          * @param {Roo.Element} el
6177          * @param {Number} rowIndex
6178          * @param {Roo.EventObject} e
6179          */
6180         "rowdblclick" : true,
6181         /**
6182          * @event mouseover
6183          * Fires when a mouseover occur
6184          * @param {Roo.bootstrap.Table} this
6185          * @param {Roo.Element} el
6186          * @param {Number} rowIndex
6187          * @param {Number} columnIndex
6188          * @param {Roo.EventObject} e
6189          */
6190         "mouseover" : true,
6191         /**
6192          * @event mouseout
6193          * Fires when a mouseout occur
6194          * @param {Roo.bootstrap.Table} this
6195          * @param {Roo.Element} el
6196          * @param {Number} rowIndex
6197          * @param {Number} columnIndex
6198          * @param {Roo.EventObject} e
6199          */
6200         "mouseout" : true,
6201         /**
6202          * @event rowclass
6203          * Fires when a row is rendered, so you can change add a style to it.
6204          * @param {Roo.bootstrap.Table} this
6205          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6206          */
6207         'rowclass' : true,
6208           /**
6209          * @event rowsrendered
6210          * Fires when all the  rows have been rendered
6211          * @param {Roo.bootstrap.Table} this
6212          */
6213         'rowsrendered' : true,
6214         /**
6215          * @event contextmenu
6216          * The raw contextmenu event for the entire grid.
6217          * @param {Roo.EventObject} e
6218          */
6219         "contextmenu" : true,
6220         /**
6221          * @event rowcontextmenu
6222          * Fires when a row is right clicked
6223          * @param {Roo.bootstrap.Table} this
6224          * @param {Number} rowIndex
6225          * @param {Roo.EventObject} e
6226          */
6227         "rowcontextmenu" : true,
6228         /**
6229          * @event cellcontextmenu
6230          * Fires when a cell is right clicked
6231          * @param {Roo.bootstrap.Table} this
6232          * @param {Number} rowIndex
6233          * @param {Number} cellIndex
6234          * @param {Roo.EventObject} e
6235          */
6236          "cellcontextmenu" : true,
6237          /**
6238          * @event headercontextmenu
6239          * Fires when a header is right clicked
6240          * @param {Roo.bootstrap.Table} this
6241          * @param {Number} columnIndex
6242          * @param {Roo.EventObject} e
6243          */
6244         "headercontextmenu" : true
6245     });
6246 };
6247
6248 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6249     
6250     cls: false,
6251     align: false,
6252     bgcolor: false,
6253     border: false,
6254     cellpadding: false,
6255     cellspacing: false,
6256     frame: false,
6257     rules: false,
6258     sortable: false,
6259     summary: false,
6260     width: false,
6261     striped : false,
6262     scrollBody : false,
6263     bordered: false,
6264     hover:  false,
6265     condensed : false,
6266     responsive : false,
6267     sm : false,
6268     cm : false,
6269     store : false,
6270     loadMask : false,
6271     footerShow : true,
6272     headerShow : true,
6273   
6274     rowSelection : false,
6275     cellSelection : false,
6276     layout : false,
6277     
6278     // Roo.Element - the tbody
6279     mainBody: false,
6280     // Roo.Element - thead element
6281     mainHead: false,
6282     
6283     container: false, // used by gridpanel...
6284     
6285     lazyLoad : false,
6286     
6287     CSS : Roo.util.CSS,
6288     
6289     auto_hide_footer : false,
6290     
6291     getAutoCreate : function()
6292     {
6293         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6294         
6295         cfg = {
6296             tag: 'table',
6297             cls : 'table',
6298             cn : []
6299         };
6300         if (this.scrollBody) {
6301             cfg.cls += ' table-body-fixed';
6302         }    
6303         if (this.striped) {
6304             cfg.cls += ' table-striped';
6305         }
6306         
6307         if (this.hover) {
6308             cfg.cls += ' table-hover';
6309         }
6310         if (this.bordered) {
6311             cfg.cls += ' table-bordered';
6312         }
6313         if (this.condensed) {
6314             cfg.cls += ' table-condensed';
6315         }
6316         if (this.responsive) {
6317             cfg.cls += ' table-responsive';
6318         }
6319         
6320         if (this.cls) {
6321             cfg.cls+=  ' ' +this.cls;
6322         }
6323         
6324         // this lot should be simplifed...
6325         var _t = this;
6326         var cp = [
6327             'align',
6328             'bgcolor',
6329             'border',
6330             'cellpadding',
6331             'cellspacing',
6332             'frame',
6333             'rules',
6334             'sortable',
6335             'summary',
6336             'width'
6337         ].forEach(function(k) {
6338             if (_t[k]) {
6339                 cfg[k] = _t[k];
6340             }
6341         });
6342         
6343         
6344         if (this.layout) {
6345             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6346         }
6347         
6348         if(this.store || this.cm){
6349             if(this.headerShow){
6350                 cfg.cn.push(this.renderHeader());
6351             }
6352             
6353             cfg.cn.push(this.renderBody());
6354             
6355             if(this.footerShow){
6356                 cfg.cn.push(this.renderFooter());
6357             }
6358             // where does this come from?
6359             //cfg.cls+=  ' TableGrid';
6360         }
6361         
6362         return { cn : [ cfg ] };
6363     },
6364     
6365     initEvents : function()
6366     {   
6367         if(!this.store || !this.cm){
6368             return;
6369         }
6370         if (this.selModel) {
6371             this.selModel.initEvents();
6372         }
6373         
6374         
6375         //Roo.log('initEvents with ds!!!!');
6376         
6377         this.mainBody = this.el.select('tbody', true).first();
6378         this.mainHead = this.el.select('thead', true).first();
6379         this.mainFoot = this.el.select('tfoot', true).first();
6380         
6381         
6382         
6383         var _this = this;
6384         
6385         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6386             e.on('click', _this.sort, _this);
6387         });
6388         
6389         this.mainBody.on("click", this.onClick, this);
6390         this.mainBody.on("dblclick", this.onDblClick, this);
6391         
6392         // why is this done????? = it breaks dialogs??
6393         //this.parent().el.setStyle('position', 'relative');
6394         
6395         
6396         if (this.footer) {
6397             this.footer.parentId = this.id;
6398             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6399             
6400             if(this.lazyLoad){
6401                 this.el.select('tfoot tr td').first().addClass('hide');
6402             }
6403         } 
6404         
6405         if(this.loadMask) {
6406             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6407         }
6408         
6409         this.store.on('load', this.onLoad, this);
6410         this.store.on('beforeload', this.onBeforeLoad, this);
6411         this.store.on('update', this.onUpdate, this);
6412         this.store.on('add', this.onAdd, this);
6413         this.store.on("clear", this.clear, this);
6414         
6415         this.el.on("contextmenu", this.onContextMenu, this);
6416         
6417         this.mainBody.on('scroll', this.onBodyScroll, this);
6418         
6419         this.cm.on("headerchange", this.onHeaderChange, this);
6420         
6421         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6422         
6423     },
6424     
6425     onContextMenu : function(e, t)
6426     {
6427         this.processEvent("contextmenu", e);
6428     },
6429     
6430     processEvent : function(name, e)
6431     {
6432         if (name != 'touchstart' ) {
6433             this.fireEvent(name, e);    
6434         }
6435         
6436         var t = e.getTarget();
6437         
6438         var cell = Roo.get(t);
6439         
6440         if(!cell){
6441             return;
6442         }
6443         
6444         if(cell.findParent('tfoot', false, true)){
6445             return;
6446         }
6447         
6448         if(cell.findParent('thead', false, true)){
6449             
6450             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6451                 cell = Roo.get(t).findParent('th', false, true);
6452                 if (!cell) {
6453                     Roo.log("failed to find th in thead?");
6454                     Roo.log(e.getTarget());
6455                     return;
6456                 }
6457             }
6458             
6459             var cellIndex = cell.dom.cellIndex;
6460             
6461             var ename = name == 'touchstart' ? 'click' : name;
6462             this.fireEvent("header" + ename, this, cellIndex, e);
6463             
6464             return;
6465         }
6466         
6467         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6468             cell = Roo.get(t).findParent('td', false, true);
6469             if (!cell) {
6470                 Roo.log("failed to find th in tbody?");
6471                 Roo.log(e.getTarget());
6472                 return;
6473             }
6474         }
6475         
6476         var row = cell.findParent('tr', false, true);
6477         var cellIndex = cell.dom.cellIndex;
6478         var rowIndex = row.dom.rowIndex - 1;
6479         
6480         if(row !== false){
6481             
6482             this.fireEvent("row" + name, this, rowIndex, e);
6483             
6484             if(cell !== false){
6485             
6486                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6487             }
6488         }
6489         
6490     },
6491     
6492     onMouseover : function(e, el)
6493     {
6494         var cell = Roo.get(el);
6495         
6496         if(!cell){
6497             return;
6498         }
6499         
6500         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6501             cell = cell.findParent('td', false, true);
6502         }
6503         
6504         var row = cell.findParent('tr', false, true);
6505         var cellIndex = cell.dom.cellIndex;
6506         var rowIndex = row.dom.rowIndex - 1; // start from 0
6507         
6508         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6509         
6510     },
6511     
6512     onMouseout : function(e, el)
6513     {
6514         var cell = Roo.get(el);
6515         
6516         if(!cell){
6517             return;
6518         }
6519         
6520         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6521             cell = cell.findParent('td', false, true);
6522         }
6523         
6524         var row = cell.findParent('tr', false, true);
6525         var cellIndex = cell.dom.cellIndex;
6526         var rowIndex = row.dom.rowIndex - 1; // start from 0
6527         
6528         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6529         
6530     },
6531     
6532     onClick : function(e, el)
6533     {
6534         var cell = Roo.get(el);
6535         
6536         if(!cell || (!this.cellSelection && !this.rowSelection)){
6537             return;
6538         }
6539         
6540         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6541             cell = cell.findParent('td', false, true);
6542         }
6543         
6544         if(!cell || typeof(cell) == 'undefined'){
6545             return;
6546         }
6547         
6548         var row = cell.findParent('tr', false, true);
6549         
6550         if(!row || typeof(row) == 'undefined'){
6551             return;
6552         }
6553         
6554         var cellIndex = cell.dom.cellIndex;
6555         var rowIndex = this.getRowIndex(row);
6556         
6557         // why??? - should these not be based on SelectionModel?
6558         if(this.cellSelection){
6559             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6560         }
6561         
6562         if(this.rowSelection){
6563             this.fireEvent('rowclick', this, row, rowIndex, e);
6564         }
6565         
6566         
6567     },
6568         
6569     onDblClick : function(e,el)
6570     {
6571         var cell = Roo.get(el);
6572         
6573         if(!cell || (!this.cellSelection && !this.rowSelection)){
6574             return;
6575         }
6576         
6577         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6578             cell = cell.findParent('td', false, true);
6579         }
6580         
6581         if(!cell || typeof(cell) == 'undefined'){
6582             return;
6583         }
6584         
6585         var row = cell.findParent('tr', false, true);
6586         
6587         if(!row || typeof(row) == 'undefined'){
6588             return;
6589         }
6590         
6591         var cellIndex = cell.dom.cellIndex;
6592         var rowIndex = this.getRowIndex(row);
6593         
6594         if(this.cellSelection){
6595             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6596         }
6597         
6598         if(this.rowSelection){
6599             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6600         }
6601     },
6602     
6603     sort : function(e,el)
6604     {
6605         var col = Roo.get(el);
6606         
6607         if(!col.hasClass('sortable')){
6608             return;
6609         }
6610         
6611         var sort = col.attr('sort');
6612         var dir = 'ASC';
6613         
6614         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6615             dir = 'DESC';
6616         }
6617         
6618         this.store.sortInfo = {field : sort, direction : dir};
6619         
6620         if (this.footer) {
6621             Roo.log("calling footer first");
6622             this.footer.onClick('first');
6623         } else {
6624         
6625             this.store.load({ params : { start : 0 } });
6626         }
6627     },
6628     
6629     renderHeader : function()
6630     {
6631         var header = {
6632             tag: 'thead',
6633             cn : []
6634         };
6635         
6636         var cm = this.cm;
6637         this.totalWidth = 0;
6638         
6639         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6640             
6641             var config = cm.config[i];
6642             
6643             var c = {
6644                 tag: 'th',
6645                 cls : 'x-hcol-' + i,
6646                 style : '',
6647                 html: cm.getColumnHeader(i)
6648             };
6649             
6650             var hh = '';
6651             
6652             if(typeof(config.sortable) != 'undefined' && config.sortable){
6653                 c.cls = 'sortable';
6654                 c.html = '<i class="glyphicon"></i>' + c.html;
6655             }
6656             
6657             if(typeof(config.lgHeader) != 'undefined'){
6658                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6659             }
6660             
6661             if(typeof(config.mdHeader) != 'undefined'){
6662                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6663             }
6664             
6665             if(typeof(config.smHeader) != 'undefined'){
6666                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6667             }
6668             
6669             if(typeof(config.xsHeader) != 'undefined'){
6670                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6671             }
6672             
6673             if(hh.length){
6674                 c.html = hh;
6675             }
6676             
6677             if(typeof(config.tooltip) != 'undefined'){
6678                 c.tooltip = config.tooltip;
6679             }
6680             
6681             if(typeof(config.colspan) != 'undefined'){
6682                 c.colspan = config.colspan;
6683             }
6684             
6685             if(typeof(config.hidden) != 'undefined' && config.hidden){
6686                 c.style += ' display:none;';
6687             }
6688             
6689             if(typeof(config.dataIndex) != 'undefined'){
6690                 c.sort = config.dataIndex;
6691             }
6692             
6693            
6694             
6695             if(typeof(config.align) != 'undefined' && config.align.length){
6696                 c.style += ' text-align:' + config.align + ';';
6697             }
6698             
6699             if(typeof(config.width) != 'undefined'){
6700                 c.style += ' width:' + config.width + 'px;';
6701                 this.totalWidth += config.width;
6702             } else {
6703                 this.totalWidth += 100; // assume minimum of 100 per column?
6704             }
6705             
6706             if(typeof(config.cls) != 'undefined'){
6707                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6708             }
6709             
6710             ['xs','sm','md','lg'].map(function(size){
6711                 
6712                 if(typeof(config[size]) == 'undefined'){
6713                     return;
6714                 }
6715                 
6716                 if (!config[size]) { // 0 = hidden
6717                     c.cls += ' hidden-' + size;
6718                     return;
6719                 }
6720                 
6721                 c.cls += ' col-' + size + '-' + config[size];
6722
6723             });
6724             
6725             header.cn.push(c)
6726         }
6727         
6728         return header;
6729     },
6730     
6731     renderBody : function()
6732     {
6733         var body = {
6734             tag: 'tbody',
6735             cn : [
6736                 {
6737                     tag: 'tr',
6738                     cn : [
6739                         {
6740                             tag : 'td',
6741                             colspan :  this.cm.getColumnCount()
6742                         }
6743                     ]
6744                 }
6745             ]
6746         };
6747         
6748         return body;
6749     },
6750     
6751     renderFooter : function()
6752     {
6753         var footer = {
6754             tag: 'tfoot',
6755             cn : [
6756                 {
6757                     tag: 'tr',
6758                     cn : [
6759                         {
6760                             tag : 'td',
6761                             colspan :  this.cm.getColumnCount()
6762                         }
6763                     ]
6764                 }
6765             ]
6766         };
6767         
6768         return footer;
6769     },
6770     
6771     
6772     
6773     onLoad : function()
6774     {
6775 //        Roo.log('ds onload');
6776         this.clear();
6777         
6778         var _this = this;
6779         var cm = this.cm;
6780         var ds = this.store;
6781         
6782         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6783             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6784             if (_this.store.sortInfo) {
6785                     
6786                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6787                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6788                 }
6789                 
6790                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6791                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6792                 }
6793             }
6794         });
6795         
6796         var tbody =  this.mainBody;
6797               
6798         if(ds.getCount() > 0){
6799             ds.data.each(function(d,rowIndex){
6800                 var row =  this.renderRow(cm, ds, rowIndex);
6801                 
6802                 tbody.createChild(row);
6803                 
6804                 var _this = this;
6805                 
6806                 if(row.cellObjects.length){
6807                     Roo.each(row.cellObjects, function(r){
6808                         _this.renderCellObject(r);
6809                     })
6810                 }
6811                 
6812             }, this);
6813         }
6814         
6815         var tfoot = this.el.select('tfoot', true).first();
6816         
6817         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6818             
6819             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6820             
6821             var total = this.ds.getTotalCount();
6822             
6823             if(this.footer.pageSize < total){
6824                 this.mainFoot.show();
6825             }
6826         }
6827         
6828         Roo.each(this.el.select('tbody td', true).elements, function(e){
6829             e.on('mouseover', _this.onMouseover, _this);
6830         });
6831         
6832         Roo.each(this.el.select('tbody td', true).elements, function(e){
6833             e.on('mouseout', _this.onMouseout, _this);
6834         });
6835         this.fireEvent('rowsrendered', this);
6836         
6837         this.autoSize();
6838     },
6839     
6840     
6841     onUpdate : function(ds,record)
6842     {
6843         this.refreshRow(record);
6844         this.autoSize();
6845     },
6846     
6847     onRemove : function(ds, record, index, isUpdate){
6848         if(isUpdate !== true){
6849             this.fireEvent("beforerowremoved", this, index, record);
6850         }
6851         var bt = this.mainBody.dom;
6852         
6853         var rows = this.el.select('tbody > tr', true).elements;
6854         
6855         if(typeof(rows[index]) != 'undefined'){
6856             bt.removeChild(rows[index].dom);
6857         }
6858         
6859 //        if(bt.rows[index]){
6860 //            bt.removeChild(bt.rows[index]);
6861 //        }
6862         
6863         if(isUpdate !== true){
6864             //this.stripeRows(index);
6865             //this.syncRowHeights(index, index);
6866             //this.layout();
6867             this.fireEvent("rowremoved", this, index, record);
6868         }
6869     },
6870     
6871     onAdd : function(ds, records, rowIndex)
6872     {
6873         //Roo.log('on Add called');
6874         // - note this does not handle multiple adding very well..
6875         var bt = this.mainBody.dom;
6876         for (var i =0 ; i < records.length;i++) {
6877             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6878             //Roo.log(records[i]);
6879             //Roo.log(this.store.getAt(rowIndex+i));
6880             this.insertRow(this.store, rowIndex + i, false);
6881             return;
6882         }
6883         
6884     },
6885     
6886     
6887     refreshRow : function(record){
6888         var ds = this.store, index;
6889         if(typeof record == 'number'){
6890             index = record;
6891             record = ds.getAt(index);
6892         }else{
6893             index = ds.indexOf(record);
6894         }
6895         this.insertRow(ds, index, true);
6896         this.autoSize();
6897         this.onRemove(ds, record, index+1, true);
6898         this.autoSize();
6899         //this.syncRowHeights(index, index);
6900         //this.layout();
6901         this.fireEvent("rowupdated", this, index, record);
6902     },
6903     
6904     insertRow : function(dm, rowIndex, isUpdate){
6905         
6906         if(!isUpdate){
6907             this.fireEvent("beforerowsinserted", this, rowIndex);
6908         }
6909             //var s = this.getScrollState();
6910         var row = this.renderRow(this.cm, this.store, rowIndex);
6911         // insert before rowIndex..
6912         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6913         
6914         var _this = this;
6915                 
6916         if(row.cellObjects.length){
6917             Roo.each(row.cellObjects, function(r){
6918                 _this.renderCellObject(r);
6919             })
6920         }
6921             
6922         if(!isUpdate){
6923             this.fireEvent("rowsinserted", this, rowIndex);
6924             //this.syncRowHeights(firstRow, lastRow);
6925             //this.stripeRows(firstRow);
6926             //this.layout();
6927         }
6928         
6929     },
6930     
6931     
6932     getRowDom : function(rowIndex)
6933     {
6934         var rows = this.el.select('tbody > tr', true).elements;
6935         
6936         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6937         
6938     },
6939     // returns the object tree for a tr..
6940   
6941     
6942     renderRow : function(cm, ds, rowIndex) 
6943     {
6944         var d = ds.getAt(rowIndex);
6945         
6946         var row = {
6947             tag : 'tr',
6948             cls : 'x-row-' + rowIndex,
6949             cn : []
6950         };
6951             
6952         var cellObjects = [];
6953         
6954         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6955             var config = cm.config[i];
6956             
6957             var renderer = cm.getRenderer(i);
6958             var value = '';
6959             var id = false;
6960             
6961             if(typeof(renderer) !== 'undefined'){
6962                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6963             }
6964             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6965             // and are rendered into the cells after the row is rendered - using the id for the element.
6966             
6967             if(typeof(value) === 'object'){
6968                 id = Roo.id();
6969                 cellObjects.push({
6970                     container : id,
6971                     cfg : value 
6972                 })
6973             }
6974             
6975             var rowcfg = {
6976                 record: d,
6977                 rowIndex : rowIndex,
6978                 colIndex : i,
6979                 rowClass : ''
6980             };
6981
6982             this.fireEvent('rowclass', this, rowcfg);
6983             
6984             var td = {
6985                 tag: 'td',
6986                 cls : rowcfg.rowClass + ' x-col-' + i,
6987                 style: '',
6988                 html: (typeof(value) === 'object') ? '' : value
6989             };
6990             
6991             if (id) {
6992                 td.id = id;
6993             }
6994             
6995             if(typeof(config.colspan) != 'undefined'){
6996                 td.colspan = config.colspan;
6997             }
6998             
6999             if(typeof(config.hidden) != 'undefined' && config.hidden){
7000                 td.style += ' display:none;';
7001             }
7002             
7003             if(typeof(config.align) != 'undefined' && config.align.length){
7004                 td.style += ' text-align:' + config.align + ';';
7005             }
7006             if(typeof(config.valign) != 'undefined' && config.valign.length){
7007                 td.style += ' vertical-align:' + config.valign + ';';
7008             }
7009             
7010             if(typeof(config.width) != 'undefined'){
7011                 td.style += ' width:' +  config.width + 'px;';
7012             }
7013             
7014             if(typeof(config.cursor) != 'undefined'){
7015                 td.style += ' cursor:' +  config.cursor + ';';
7016             }
7017             
7018             if(typeof(config.cls) != 'undefined'){
7019                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7020             }
7021             
7022             ['xs','sm','md','lg'].map(function(size){
7023                 
7024                 if(typeof(config[size]) == 'undefined'){
7025                     return;
7026                 }
7027                 
7028                 if (!config[size]) { // 0 = hidden
7029                     td.cls += ' hidden-' + size;
7030                     return;
7031                 }
7032                 
7033                 td.cls += ' col-' + size + '-' + config[size];
7034
7035             });
7036             
7037             row.cn.push(td);
7038            
7039         }
7040         
7041         row.cellObjects = cellObjects;
7042         
7043         return row;
7044           
7045     },
7046     
7047     
7048     
7049     onBeforeLoad : function()
7050     {
7051         
7052     },
7053      /**
7054      * Remove all rows
7055      */
7056     clear : function()
7057     {
7058         this.el.select('tbody', true).first().dom.innerHTML = '';
7059     },
7060     /**
7061      * Show or hide a row.
7062      * @param {Number} rowIndex to show or hide
7063      * @param {Boolean} state hide
7064      */
7065     setRowVisibility : function(rowIndex, state)
7066     {
7067         var bt = this.mainBody.dom;
7068         
7069         var rows = this.el.select('tbody > tr', true).elements;
7070         
7071         if(typeof(rows[rowIndex]) == 'undefined'){
7072             return;
7073         }
7074         rows[rowIndex].dom.style.display = state ? '' : 'none';
7075     },
7076     
7077     
7078     getSelectionModel : function(){
7079         if(!this.selModel){
7080             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7081         }
7082         return this.selModel;
7083     },
7084     /*
7085      * Render the Roo.bootstrap object from renderder
7086      */
7087     renderCellObject : function(r)
7088     {
7089         var _this = this;
7090         
7091         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7092         
7093         var t = r.cfg.render(r.container);
7094         
7095         if(r.cfg.cn){
7096             Roo.each(r.cfg.cn, function(c){
7097                 var child = {
7098                     container: t.getChildContainer(),
7099                     cfg: c
7100                 };
7101                 _this.renderCellObject(child);
7102             })
7103         }
7104     },
7105     
7106     getRowIndex : function(row)
7107     {
7108         var rowIndex = -1;
7109         
7110         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7111             if(el != row){
7112                 return;
7113             }
7114             
7115             rowIndex = index;
7116         });
7117         
7118         return rowIndex;
7119     },
7120      /**
7121      * Returns the grid's underlying element = used by panel.Grid
7122      * @return {Element} The element
7123      */
7124     getGridEl : function(){
7125         return this.el;
7126     },
7127      /**
7128      * Forces a resize - used by panel.Grid
7129      * @return {Element} The element
7130      */
7131     autoSize : function()
7132     {
7133         //var ctr = Roo.get(this.container.dom.parentElement);
7134         var ctr = Roo.get(this.el.dom);
7135         
7136         var thd = this.getGridEl().select('thead',true).first();
7137         var tbd = this.getGridEl().select('tbody', true).first();
7138         var tfd = this.getGridEl().select('tfoot', true).first();
7139         
7140         var cw = ctr.getWidth();
7141         
7142         if (tbd) {
7143             
7144             tbd.setSize(ctr.getWidth(),
7145                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7146             );
7147             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7148             cw -= barsize;
7149         }
7150         cw = Math.max(cw, this.totalWidth);
7151         this.getGridEl().select('tr',true).setWidth(cw);
7152         // resize 'expandable coloumn?
7153         
7154         return; // we doe not have a view in this design..
7155         
7156     },
7157     onBodyScroll: function()
7158     {
7159         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7160         if(this.mainHead){
7161             this.mainHead.setStyle({
7162                 'position' : 'relative',
7163                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7164             });
7165         }
7166         
7167         if(this.lazyLoad){
7168             
7169             var scrollHeight = this.mainBody.dom.scrollHeight;
7170             
7171             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7172             
7173             var height = this.mainBody.getHeight();
7174             
7175             if(scrollHeight - height == scrollTop) {
7176                 
7177                 var total = this.ds.getTotalCount();
7178                 
7179                 if(this.footer.cursor + this.footer.pageSize < total){
7180                     
7181                     this.footer.ds.load({
7182                         params : {
7183                             start : this.footer.cursor + this.footer.pageSize,
7184                             limit : this.footer.pageSize
7185                         },
7186                         add : true
7187                     });
7188                 }
7189             }
7190             
7191         }
7192     },
7193     
7194     onHeaderChange : function()
7195     {
7196         var header = this.renderHeader();
7197         var table = this.el.select('table', true).first();
7198         
7199         this.mainHead.remove();
7200         this.mainHead = table.createChild(header, this.mainBody, false);
7201     },
7202     
7203     onHiddenChange : function(colModel, colIndex, hidden)
7204     {
7205         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7206         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7207         
7208         this.CSS.updateRule(thSelector, "display", "");
7209         this.CSS.updateRule(tdSelector, "display", "");
7210         
7211         if(hidden){
7212             this.CSS.updateRule(thSelector, "display", "none");
7213             this.CSS.updateRule(tdSelector, "display", "none");
7214         }
7215         
7216         this.onHeaderChange();
7217         this.onLoad();
7218     },
7219     
7220     setColumnWidth: function(col_index, width)
7221     {
7222         // width = "md-2 xs-2..."
7223         if(!this.colModel.config[col_index]) {
7224             return;
7225         }
7226         
7227         var w = width.split(" ");
7228         
7229         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7230         
7231         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7232         
7233         
7234         for(var j = 0; j < w.length; j++) {
7235             
7236             if(!w[j]) {
7237                 continue;
7238             }
7239             
7240             var size_cls = w[j].split("-");
7241             
7242             if(!Number.isInteger(size_cls[1] * 1)) {
7243                 continue;
7244             }
7245             
7246             if(!this.colModel.config[col_index][size_cls[0]]) {
7247                 continue;
7248             }
7249             
7250             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7251                 continue;
7252             }
7253             
7254             h_row[0].classList.replace(
7255                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7256                 "col-"+size_cls[0]+"-"+size_cls[1]
7257             );
7258             
7259             for(var i = 0; i < rows.length; i++) {
7260                 
7261                 var size_cls = w[j].split("-");
7262                 
7263                 if(!Number.isInteger(size_cls[1] * 1)) {
7264                     continue;
7265                 }
7266                 
7267                 if(!this.colModel.config[col_index][size_cls[0]]) {
7268                     continue;
7269                 }
7270                 
7271                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7272                     continue;
7273                 }
7274                 
7275                 rows[i].classList.replace(
7276                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7277                     "col-"+size_cls[0]+"-"+size_cls[1]
7278                 );
7279             }
7280             
7281             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7282         }
7283     }
7284 });
7285
7286  
7287
7288  /*
7289  * - LGPL
7290  *
7291  * table cell
7292  * 
7293  */
7294
7295 /**
7296  * @class Roo.bootstrap.TableCell
7297  * @extends Roo.bootstrap.Component
7298  * Bootstrap TableCell class
7299  * @cfg {String} html cell contain text
7300  * @cfg {String} cls cell class
7301  * @cfg {String} tag cell tag (td|th) default td
7302  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7303  * @cfg {String} align Aligns the content in a cell
7304  * @cfg {String} axis Categorizes cells
7305  * @cfg {String} bgcolor Specifies the background color of a cell
7306  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7307  * @cfg {Number} colspan Specifies the number of columns a cell should span
7308  * @cfg {String} headers Specifies one or more header cells a cell is related to
7309  * @cfg {Number} height Sets the height of a cell
7310  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7311  * @cfg {Number} rowspan Sets the number of rows a cell should span
7312  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7313  * @cfg {String} valign Vertical aligns the content in a cell
7314  * @cfg {Number} width Specifies the width of a cell
7315  * 
7316  * @constructor
7317  * Create a new TableCell
7318  * @param {Object} config The config object
7319  */
7320
7321 Roo.bootstrap.TableCell = function(config){
7322     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7323 };
7324
7325 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7326     
7327     html: false,
7328     cls: false,
7329     tag: false,
7330     abbr: false,
7331     align: false,
7332     axis: false,
7333     bgcolor: false,
7334     charoff: false,
7335     colspan: false,
7336     headers: false,
7337     height: false,
7338     nowrap: false,
7339     rowspan: false,
7340     scope: false,
7341     valign: false,
7342     width: false,
7343     
7344     
7345     getAutoCreate : function(){
7346         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7347         
7348         cfg = {
7349             tag: 'td'
7350         };
7351         
7352         if(this.tag){
7353             cfg.tag = this.tag;
7354         }
7355         
7356         if (this.html) {
7357             cfg.html=this.html
7358         }
7359         if (this.cls) {
7360             cfg.cls=this.cls
7361         }
7362         if (this.abbr) {
7363             cfg.abbr=this.abbr
7364         }
7365         if (this.align) {
7366             cfg.align=this.align
7367         }
7368         if (this.axis) {
7369             cfg.axis=this.axis
7370         }
7371         if (this.bgcolor) {
7372             cfg.bgcolor=this.bgcolor
7373         }
7374         if (this.charoff) {
7375             cfg.charoff=this.charoff
7376         }
7377         if (this.colspan) {
7378             cfg.colspan=this.colspan
7379         }
7380         if (this.headers) {
7381             cfg.headers=this.headers
7382         }
7383         if (this.height) {
7384             cfg.height=this.height
7385         }
7386         if (this.nowrap) {
7387             cfg.nowrap=this.nowrap
7388         }
7389         if (this.rowspan) {
7390             cfg.rowspan=this.rowspan
7391         }
7392         if (this.scope) {
7393             cfg.scope=this.scope
7394         }
7395         if (this.valign) {
7396             cfg.valign=this.valign
7397         }
7398         if (this.width) {
7399             cfg.width=this.width
7400         }
7401         
7402         
7403         return cfg;
7404     }
7405    
7406 });
7407
7408  
7409
7410  /*
7411  * - LGPL
7412  *
7413  * table row
7414  * 
7415  */
7416
7417 /**
7418  * @class Roo.bootstrap.TableRow
7419  * @extends Roo.bootstrap.Component
7420  * Bootstrap TableRow class
7421  * @cfg {String} cls row class
7422  * @cfg {String} align Aligns the content in a table row
7423  * @cfg {String} bgcolor Specifies a background color for a table row
7424  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7425  * @cfg {String} valign Vertical aligns the content in a table row
7426  * 
7427  * @constructor
7428  * Create a new TableRow
7429  * @param {Object} config The config object
7430  */
7431
7432 Roo.bootstrap.TableRow = function(config){
7433     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7434 };
7435
7436 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7437     
7438     cls: false,
7439     align: false,
7440     bgcolor: false,
7441     charoff: false,
7442     valign: false,
7443     
7444     getAutoCreate : function(){
7445         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7446         
7447         cfg = {
7448             tag: 'tr'
7449         };
7450             
7451         if(this.cls){
7452             cfg.cls = this.cls;
7453         }
7454         if(this.align){
7455             cfg.align = this.align;
7456         }
7457         if(this.bgcolor){
7458             cfg.bgcolor = this.bgcolor;
7459         }
7460         if(this.charoff){
7461             cfg.charoff = this.charoff;
7462         }
7463         if(this.valign){
7464             cfg.valign = this.valign;
7465         }
7466         
7467         return cfg;
7468     }
7469    
7470 });
7471
7472  
7473
7474  /*
7475  * - LGPL
7476  *
7477  * table body
7478  * 
7479  */
7480
7481 /**
7482  * @class Roo.bootstrap.TableBody
7483  * @extends Roo.bootstrap.Component
7484  * Bootstrap TableBody class
7485  * @cfg {String} cls element class
7486  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7487  * @cfg {String} align Aligns the content inside the element
7488  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7489  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7490  * 
7491  * @constructor
7492  * Create a new TableBody
7493  * @param {Object} config The config object
7494  */
7495
7496 Roo.bootstrap.TableBody = function(config){
7497     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7498 };
7499
7500 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7501     
7502     cls: false,
7503     tag: false,
7504     align: false,
7505     charoff: false,
7506     valign: false,
7507     
7508     getAutoCreate : function(){
7509         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7510         
7511         cfg = {
7512             tag: 'tbody'
7513         };
7514             
7515         if (this.cls) {
7516             cfg.cls=this.cls
7517         }
7518         if(this.tag){
7519             cfg.tag = this.tag;
7520         }
7521         
7522         if(this.align){
7523             cfg.align = this.align;
7524         }
7525         if(this.charoff){
7526             cfg.charoff = this.charoff;
7527         }
7528         if(this.valign){
7529             cfg.valign = this.valign;
7530         }
7531         
7532         return cfg;
7533     }
7534     
7535     
7536 //    initEvents : function()
7537 //    {
7538 //        
7539 //        if(!this.store){
7540 //            return;
7541 //        }
7542 //        
7543 //        this.store = Roo.factory(this.store, Roo.data);
7544 //        this.store.on('load', this.onLoad, this);
7545 //        
7546 //        this.store.load();
7547 //        
7548 //    },
7549 //    
7550 //    onLoad: function () 
7551 //    {   
7552 //        this.fireEvent('load', this);
7553 //    }
7554 //    
7555 //   
7556 });
7557
7558  
7559
7560  /*
7561  * Based on:
7562  * Ext JS Library 1.1.1
7563  * Copyright(c) 2006-2007, Ext JS, LLC.
7564  *
7565  * Originally Released Under LGPL - original licence link has changed is not relivant.
7566  *
7567  * Fork - LGPL
7568  * <script type="text/javascript">
7569  */
7570
7571 // as we use this in bootstrap.
7572 Roo.namespace('Roo.form');
7573  /**
7574  * @class Roo.form.Action
7575  * Internal Class used to handle form actions
7576  * @constructor
7577  * @param {Roo.form.BasicForm} el The form element or its id
7578  * @param {Object} config Configuration options
7579  */
7580
7581  
7582  
7583 // define the action interface
7584 Roo.form.Action = function(form, options){
7585     this.form = form;
7586     this.options = options || {};
7587 };
7588 /**
7589  * Client Validation Failed
7590  * @const 
7591  */
7592 Roo.form.Action.CLIENT_INVALID = 'client';
7593 /**
7594  * Server Validation Failed
7595  * @const 
7596  */
7597 Roo.form.Action.SERVER_INVALID = 'server';
7598  /**
7599  * Connect to Server Failed
7600  * @const 
7601  */
7602 Roo.form.Action.CONNECT_FAILURE = 'connect';
7603 /**
7604  * Reading Data from Server Failed
7605  * @const 
7606  */
7607 Roo.form.Action.LOAD_FAILURE = 'load';
7608
7609 Roo.form.Action.prototype = {
7610     type : 'default',
7611     failureType : undefined,
7612     response : undefined,
7613     result : undefined,
7614
7615     // interface method
7616     run : function(options){
7617
7618     },
7619
7620     // interface method
7621     success : function(response){
7622
7623     },
7624
7625     // interface method
7626     handleResponse : function(response){
7627
7628     },
7629
7630     // default connection failure
7631     failure : function(response){
7632         
7633         this.response = response;
7634         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7635         this.form.afterAction(this, false);
7636     },
7637
7638     processResponse : function(response){
7639         this.response = response;
7640         if(!response.responseText){
7641             return true;
7642         }
7643         this.result = this.handleResponse(response);
7644         return this.result;
7645     },
7646
7647     // utility functions used internally
7648     getUrl : function(appendParams){
7649         var url = this.options.url || this.form.url || this.form.el.dom.action;
7650         if(appendParams){
7651             var p = this.getParams();
7652             if(p){
7653                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7654             }
7655         }
7656         return url;
7657     },
7658
7659     getMethod : function(){
7660         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7661     },
7662
7663     getParams : function(){
7664         var bp = this.form.baseParams;
7665         var p = this.options.params;
7666         if(p){
7667             if(typeof p == "object"){
7668                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7669             }else if(typeof p == 'string' && bp){
7670                 p += '&' + Roo.urlEncode(bp);
7671             }
7672         }else if(bp){
7673             p = Roo.urlEncode(bp);
7674         }
7675         return p;
7676     },
7677
7678     createCallback : function(){
7679         return {
7680             success: this.success,
7681             failure: this.failure,
7682             scope: this,
7683             timeout: (this.form.timeout*1000),
7684             upload: this.form.fileUpload ? this.success : undefined
7685         };
7686     }
7687 };
7688
7689 Roo.form.Action.Submit = function(form, options){
7690     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7691 };
7692
7693 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7694     type : 'submit',
7695
7696     haveProgress : false,
7697     uploadComplete : false,
7698     
7699     // uploadProgress indicator.
7700     uploadProgress : function()
7701     {
7702         if (!this.form.progressUrl) {
7703             return;
7704         }
7705         
7706         if (!this.haveProgress) {
7707             Roo.MessageBox.progress("Uploading", "Uploading");
7708         }
7709         if (this.uploadComplete) {
7710            Roo.MessageBox.hide();
7711            return;
7712         }
7713         
7714         this.haveProgress = true;
7715    
7716         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7717         
7718         var c = new Roo.data.Connection();
7719         c.request({
7720             url : this.form.progressUrl,
7721             params: {
7722                 id : uid
7723             },
7724             method: 'GET',
7725             success : function(req){
7726                //console.log(data);
7727                 var rdata = false;
7728                 var edata;
7729                 try  {
7730                    rdata = Roo.decode(req.responseText)
7731                 } catch (e) {
7732                     Roo.log("Invalid data from server..");
7733                     Roo.log(edata);
7734                     return;
7735                 }
7736                 if (!rdata || !rdata.success) {
7737                     Roo.log(rdata);
7738                     Roo.MessageBox.alert(Roo.encode(rdata));
7739                     return;
7740                 }
7741                 var data = rdata.data;
7742                 
7743                 if (this.uploadComplete) {
7744                    Roo.MessageBox.hide();
7745                    return;
7746                 }
7747                    
7748                 if (data){
7749                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7750                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7751                     );
7752                 }
7753                 this.uploadProgress.defer(2000,this);
7754             },
7755        
7756             failure: function(data) {
7757                 Roo.log('progress url failed ');
7758                 Roo.log(data);
7759             },
7760             scope : this
7761         });
7762            
7763     },
7764     
7765     
7766     run : function()
7767     {
7768         // run get Values on the form, so it syncs any secondary forms.
7769         this.form.getValues();
7770         
7771         var o = this.options;
7772         var method = this.getMethod();
7773         var isPost = method == 'POST';
7774         if(o.clientValidation === false || this.form.isValid()){
7775             
7776             if (this.form.progressUrl) {
7777                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7778                     (new Date() * 1) + '' + Math.random());
7779                     
7780             } 
7781             
7782             
7783             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7784                 form:this.form.el.dom,
7785                 url:this.getUrl(!isPost),
7786                 method: method,
7787                 params:isPost ? this.getParams() : null,
7788                 isUpload: this.form.fileUpload
7789             }));
7790             
7791             this.uploadProgress();
7792
7793         }else if (o.clientValidation !== false){ // client validation failed
7794             this.failureType = Roo.form.Action.CLIENT_INVALID;
7795             this.form.afterAction(this, false);
7796         }
7797     },
7798
7799     success : function(response)
7800     {
7801         this.uploadComplete= true;
7802         if (this.haveProgress) {
7803             Roo.MessageBox.hide();
7804         }
7805         
7806         
7807         var result = this.processResponse(response);
7808         if(result === true || result.success){
7809             this.form.afterAction(this, true);
7810             return;
7811         }
7812         if(result.errors){
7813             this.form.markInvalid(result.errors);
7814             this.failureType = Roo.form.Action.SERVER_INVALID;
7815         }
7816         this.form.afterAction(this, false);
7817     },
7818     failure : function(response)
7819     {
7820         this.uploadComplete= true;
7821         if (this.haveProgress) {
7822             Roo.MessageBox.hide();
7823         }
7824         
7825         this.response = response;
7826         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7827         this.form.afterAction(this, false);
7828     },
7829     
7830     handleResponse : function(response){
7831         if(this.form.errorReader){
7832             var rs = this.form.errorReader.read(response);
7833             var errors = [];
7834             if(rs.records){
7835                 for(var i = 0, len = rs.records.length; i < len; i++) {
7836                     var r = rs.records[i];
7837                     errors[i] = r.data;
7838                 }
7839             }
7840             if(errors.length < 1){
7841                 errors = null;
7842             }
7843             return {
7844                 success : rs.success,
7845                 errors : errors
7846             };
7847         }
7848         var ret = false;
7849         try {
7850             ret = Roo.decode(response.responseText);
7851         } catch (e) {
7852             ret = {
7853                 success: false,
7854                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7855                 errors : []
7856             };
7857         }
7858         return ret;
7859         
7860     }
7861 });
7862
7863
7864 Roo.form.Action.Load = function(form, options){
7865     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7866     this.reader = this.form.reader;
7867 };
7868
7869 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7870     type : 'load',
7871
7872     run : function(){
7873         
7874         Roo.Ajax.request(Roo.apply(
7875                 this.createCallback(), {
7876                     method:this.getMethod(),
7877                     url:this.getUrl(false),
7878                     params:this.getParams()
7879         }));
7880     },
7881
7882     success : function(response){
7883         
7884         var result = this.processResponse(response);
7885         if(result === true || !result.success || !result.data){
7886             this.failureType = Roo.form.Action.LOAD_FAILURE;
7887             this.form.afterAction(this, false);
7888             return;
7889         }
7890         this.form.clearInvalid();
7891         this.form.setValues(result.data);
7892         this.form.afterAction(this, true);
7893     },
7894
7895     handleResponse : function(response){
7896         if(this.form.reader){
7897             var rs = this.form.reader.read(response);
7898             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7899             return {
7900                 success : rs.success,
7901                 data : data
7902             };
7903         }
7904         return Roo.decode(response.responseText);
7905     }
7906 });
7907
7908 Roo.form.Action.ACTION_TYPES = {
7909     'load' : Roo.form.Action.Load,
7910     'submit' : Roo.form.Action.Submit
7911 };/*
7912  * - LGPL
7913  *
7914  * form
7915  *
7916  */
7917
7918 /**
7919  * @class Roo.bootstrap.Form
7920  * @extends Roo.bootstrap.Component
7921  * Bootstrap Form class
7922  * @cfg {String} method  GET | POST (default POST)
7923  * @cfg {String} labelAlign top | left (default top)
7924  * @cfg {String} align left  | right - for navbars
7925  * @cfg {Boolean} loadMask load mask when submit (default true)
7926
7927  *
7928  * @constructor
7929  * Create a new Form
7930  * @param {Object} config The config object
7931  */
7932
7933
7934 Roo.bootstrap.Form = function(config){
7935     
7936     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7937     
7938     Roo.bootstrap.Form.popover.apply();
7939     
7940     this.addEvents({
7941         /**
7942          * @event clientvalidation
7943          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7944          * @param {Form} this
7945          * @param {Boolean} valid true if the form has passed client-side validation
7946          */
7947         clientvalidation: true,
7948         /**
7949          * @event beforeaction
7950          * Fires before any action is performed. Return false to cancel the action.
7951          * @param {Form} this
7952          * @param {Action} action The action to be performed
7953          */
7954         beforeaction: true,
7955         /**
7956          * @event actionfailed
7957          * Fires when an action fails.
7958          * @param {Form} this
7959          * @param {Action} action The action that failed
7960          */
7961         actionfailed : true,
7962         /**
7963          * @event actioncomplete
7964          * Fires when an action is completed.
7965          * @param {Form} this
7966          * @param {Action} action The action that completed
7967          */
7968         actioncomplete : true
7969     });
7970 };
7971
7972 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7973
7974      /**
7975      * @cfg {String} method
7976      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7977      */
7978     method : 'POST',
7979     /**
7980      * @cfg {String} url
7981      * The URL to use for form actions if one isn't supplied in the action options.
7982      */
7983     /**
7984      * @cfg {Boolean} fileUpload
7985      * Set to true if this form is a file upload.
7986      */
7987
7988     /**
7989      * @cfg {Object} baseParams
7990      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7991      */
7992
7993     /**
7994      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7995      */
7996     timeout: 30,
7997     /**
7998      * @cfg {Sting} align (left|right) for navbar forms
7999      */
8000     align : 'left',
8001
8002     // private
8003     activeAction : null,
8004
8005     /**
8006      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8007      * element by passing it or its id or mask the form itself by passing in true.
8008      * @type Mixed
8009      */
8010     waitMsgTarget : false,
8011
8012     loadMask : true,
8013     
8014     /**
8015      * @cfg {Boolean} errorMask (true|false) default false
8016      */
8017     errorMask : false,
8018     
8019     /**
8020      * @cfg {Number} maskOffset Default 100
8021      */
8022     maskOffset : 100,
8023     
8024     /**
8025      * @cfg {Boolean} maskBody
8026      */
8027     maskBody : false,
8028
8029     getAutoCreate : function(){
8030
8031         var cfg = {
8032             tag: 'form',
8033             method : this.method || 'POST',
8034             id : this.id || Roo.id(),
8035             cls : ''
8036         };
8037         if (this.parent().xtype.match(/^Nav/)) {
8038             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8039
8040         }
8041
8042         if (this.labelAlign == 'left' ) {
8043             cfg.cls += ' form-horizontal';
8044         }
8045
8046
8047         return cfg;
8048     },
8049     initEvents : function()
8050     {
8051         this.el.on('submit', this.onSubmit, this);
8052         // this was added as random key presses on the form where triggering form submit.
8053         this.el.on('keypress', function(e) {
8054             if (e.getCharCode() != 13) {
8055                 return true;
8056             }
8057             // we might need to allow it for textareas.. and some other items.
8058             // check e.getTarget().
8059
8060             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8061                 return true;
8062             }
8063
8064             Roo.log("keypress blocked");
8065
8066             e.preventDefault();
8067             return false;
8068         });
8069         
8070     },
8071     // private
8072     onSubmit : function(e){
8073         e.stopEvent();
8074     },
8075
8076      /**
8077      * Returns true if client-side validation on the form is successful.
8078      * @return Boolean
8079      */
8080     isValid : function(){
8081         var items = this.getItems();
8082         var valid = true;
8083         var target = false;
8084         
8085         items.each(function(f){
8086             
8087             if(f.validate()){
8088                 return;
8089             }
8090             
8091             Roo.log('invalid field: ' + f.name);
8092             
8093             valid = false;
8094
8095             if(!target && f.el.isVisible(true)){
8096                 target = f;
8097             }
8098            
8099         });
8100         
8101         if(this.errorMask && !valid){
8102             Roo.bootstrap.Form.popover.mask(this, target);
8103         }
8104         
8105         return valid;
8106     },
8107     
8108     /**
8109      * Returns true if any fields in this form have changed since their original load.
8110      * @return Boolean
8111      */
8112     isDirty : function(){
8113         var dirty = false;
8114         var items = this.getItems();
8115         items.each(function(f){
8116            if(f.isDirty()){
8117                dirty = true;
8118                return false;
8119            }
8120            return true;
8121         });
8122         return dirty;
8123     },
8124      /**
8125      * Performs a predefined action (submit or load) or custom actions you define on this form.
8126      * @param {String} actionName The name of the action type
8127      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8128      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8129      * accept other config options):
8130      * <pre>
8131 Property          Type             Description
8132 ----------------  ---------------  ----------------------------------------------------------------------------------
8133 url               String           The url for the action (defaults to the form's url)
8134 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8135 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8136 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8137                                    validate the form on the client (defaults to false)
8138      * </pre>
8139      * @return {BasicForm} this
8140      */
8141     doAction : function(action, options){
8142         if(typeof action == 'string'){
8143             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8144         }
8145         if(this.fireEvent('beforeaction', this, action) !== false){
8146             this.beforeAction(action);
8147             action.run.defer(100, action);
8148         }
8149         return this;
8150     },
8151
8152     // private
8153     beforeAction : function(action){
8154         var o = action.options;
8155         
8156         if(this.loadMask){
8157             
8158             if(this.maskBody){
8159                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8160             } else {
8161                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8162             }
8163         }
8164         // not really supported yet.. ??
8165
8166         //if(this.waitMsgTarget === true){
8167         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8168         //}else if(this.waitMsgTarget){
8169         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8170         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8171         //}else {
8172         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8173        // }
8174
8175     },
8176
8177     // private
8178     afterAction : function(action, success){
8179         this.activeAction = null;
8180         var o = action.options;
8181
8182         if(this.loadMask){
8183             
8184             if(this.maskBody){
8185                 Roo.get(document.body).unmask();
8186             } else {
8187                 this.el.unmask();
8188             }
8189         }
8190         
8191         //if(this.waitMsgTarget === true){
8192 //            this.el.unmask();
8193         //}else if(this.waitMsgTarget){
8194         //    this.waitMsgTarget.unmask();
8195         //}else{
8196         //    Roo.MessageBox.updateProgress(1);
8197         //    Roo.MessageBox.hide();
8198        // }
8199         //
8200         if(success){
8201             if(o.reset){
8202                 this.reset();
8203             }
8204             Roo.callback(o.success, o.scope, [this, action]);
8205             this.fireEvent('actioncomplete', this, action);
8206
8207         }else{
8208
8209             // failure condition..
8210             // we have a scenario where updates need confirming.
8211             // eg. if a locking scenario exists..
8212             // we look for { errors : { needs_confirm : true }} in the response.
8213             if (
8214                 (typeof(action.result) != 'undefined')  &&
8215                 (typeof(action.result.errors) != 'undefined')  &&
8216                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8217            ){
8218                 var _t = this;
8219                 Roo.log("not supported yet");
8220                  /*
8221
8222                 Roo.MessageBox.confirm(
8223                     "Change requires confirmation",
8224                     action.result.errorMsg,
8225                     function(r) {
8226                         if (r != 'yes') {
8227                             return;
8228                         }
8229                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8230                     }
8231
8232                 );
8233                 */
8234
8235
8236                 return;
8237             }
8238
8239             Roo.callback(o.failure, o.scope, [this, action]);
8240             // show an error message if no failed handler is set..
8241             if (!this.hasListener('actionfailed')) {
8242                 Roo.log("need to add dialog support");
8243                 /*
8244                 Roo.MessageBox.alert("Error",
8245                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8246                         action.result.errorMsg :
8247                         "Saving Failed, please check your entries or try again"
8248                 );
8249                 */
8250             }
8251
8252             this.fireEvent('actionfailed', this, action);
8253         }
8254
8255     },
8256     /**
8257      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8258      * @param {String} id The value to search for
8259      * @return Field
8260      */
8261     findField : function(id){
8262         var items = this.getItems();
8263         var field = items.get(id);
8264         if(!field){
8265              items.each(function(f){
8266                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8267                     field = f;
8268                     return false;
8269                 }
8270                 return true;
8271             });
8272         }
8273         return field || null;
8274     },
8275      /**
8276      * Mark fields in this form invalid in bulk.
8277      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8278      * @return {BasicForm} this
8279      */
8280     markInvalid : function(errors){
8281         if(errors instanceof Array){
8282             for(var i = 0, len = errors.length; i < len; i++){
8283                 var fieldError = errors[i];
8284                 var f = this.findField(fieldError.id);
8285                 if(f){
8286                     f.markInvalid(fieldError.msg);
8287                 }
8288             }
8289         }else{
8290             var field, id;
8291             for(id in errors){
8292                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8293                     field.markInvalid(errors[id]);
8294                 }
8295             }
8296         }
8297         //Roo.each(this.childForms || [], function (f) {
8298         //    f.markInvalid(errors);
8299         //});
8300
8301         return this;
8302     },
8303
8304     /**
8305      * Set values for fields in this form in bulk.
8306      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8307      * @return {BasicForm} this
8308      */
8309     setValues : function(values){
8310         if(values instanceof Array){ // array of objects
8311             for(var i = 0, len = values.length; i < len; i++){
8312                 var v = values[i];
8313                 var f = this.findField(v.id);
8314                 if(f){
8315                     f.setValue(v.value);
8316                     if(this.trackResetOnLoad){
8317                         f.originalValue = f.getValue();
8318                     }
8319                 }
8320             }
8321         }else{ // object hash
8322             var field, id;
8323             for(id in values){
8324                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8325
8326                     if (field.setFromData &&
8327                         field.valueField &&
8328                         field.displayField &&
8329                         // combos' with local stores can
8330                         // be queried via setValue()
8331                         // to set their value..
8332                         (field.store && !field.store.isLocal)
8333                         ) {
8334                         // it's a combo
8335                         var sd = { };
8336                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8337                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8338                         field.setFromData(sd);
8339
8340                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8341                         
8342                         field.setFromData(values);
8343                         
8344                     } else {
8345                         field.setValue(values[id]);
8346                     }
8347
8348
8349                     if(this.trackResetOnLoad){
8350                         field.originalValue = field.getValue();
8351                     }
8352                 }
8353             }
8354         }
8355
8356         //Roo.each(this.childForms || [], function (f) {
8357         //    f.setValues(values);
8358         //});
8359
8360         return this;
8361     },
8362
8363     /**
8364      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8365      * they are returned as an array.
8366      * @param {Boolean} asString
8367      * @return {Object}
8368      */
8369     getValues : function(asString){
8370         //if (this.childForms) {
8371             // copy values from the child forms
8372         //    Roo.each(this.childForms, function (f) {
8373         //        this.setValues(f.getValues());
8374         //    }, this);
8375         //}
8376
8377
8378
8379         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8380         if(asString === true){
8381             return fs;
8382         }
8383         return Roo.urlDecode(fs);
8384     },
8385
8386     /**
8387      * Returns the fields in this form as an object with key/value pairs.
8388      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8389      * @return {Object}
8390      */
8391     getFieldValues : function(with_hidden)
8392     {
8393         var items = this.getItems();
8394         var ret = {};
8395         items.each(function(f){
8396             
8397             if (!f.getName()) {
8398                 return;
8399             }
8400             
8401             var v = f.getValue();
8402             
8403             if (f.inputType =='radio') {
8404                 if (typeof(ret[f.getName()]) == 'undefined') {
8405                     ret[f.getName()] = ''; // empty..
8406                 }
8407
8408                 if (!f.el.dom.checked) {
8409                     return;
8410
8411                 }
8412                 v = f.el.dom.value;
8413
8414             }
8415             
8416             if(f.xtype == 'MoneyField'){
8417                 ret[f.currencyName] = f.getCurrency();
8418             }
8419
8420             // not sure if this supported any more..
8421             if ((typeof(v) == 'object') && f.getRawValue) {
8422                 v = f.getRawValue() ; // dates..
8423             }
8424             // combo boxes where name != hiddenName...
8425             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8426                 ret[f.name] = f.getRawValue();
8427             }
8428             ret[f.getName()] = v;
8429         });
8430
8431         return ret;
8432     },
8433
8434     /**
8435      * Clears all invalid messages in this form.
8436      * @return {BasicForm} this
8437      */
8438     clearInvalid : function(){
8439         var items = this.getItems();
8440
8441         items.each(function(f){
8442            f.clearInvalid();
8443         });
8444
8445         return this;
8446     },
8447
8448     /**
8449      * Resets this form.
8450      * @return {BasicForm} this
8451      */
8452     reset : function(){
8453         var items = this.getItems();
8454         items.each(function(f){
8455             f.reset();
8456         });
8457
8458         Roo.each(this.childForms || [], function (f) {
8459             f.reset();
8460         });
8461
8462
8463         return this;
8464     },
8465     
8466     getItems : function()
8467     {
8468         var r=new Roo.util.MixedCollection(false, function(o){
8469             return o.id || (o.id = Roo.id());
8470         });
8471         var iter = function(el) {
8472             if (el.inputEl) {
8473                 r.add(el);
8474             }
8475             if (!el.items) {
8476                 return;
8477             }
8478             Roo.each(el.items,function(e) {
8479                 iter(e);
8480             });
8481         };
8482
8483         iter(this);
8484         return r;
8485     },
8486     
8487     hideFields : function(items)
8488     {
8489         Roo.each(items, function(i){
8490             
8491             var f = this.findField(i);
8492             
8493             if(!f){
8494                 return;
8495             }
8496             
8497             f.hide();
8498             
8499         }, this);
8500     },
8501     
8502     showFields : function(items)
8503     {
8504         Roo.each(items, function(i){
8505             
8506             var f = this.findField(i);
8507             
8508             if(!f){
8509                 return;
8510             }
8511             
8512             f.show();
8513             
8514         }, this);
8515     }
8516
8517 });
8518
8519 Roo.apply(Roo.bootstrap.Form, {
8520     
8521     popover : {
8522         
8523         padding : 5,
8524         
8525         isApplied : false,
8526         
8527         isMasked : false,
8528         
8529         form : false,
8530         
8531         target : false,
8532         
8533         toolTip : false,
8534         
8535         intervalID : false,
8536         
8537         maskEl : false,
8538         
8539         apply : function()
8540         {
8541             if(this.isApplied){
8542                 return;
8543             }
8544             
8545             this.maskEl = {
8546                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8547                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8548                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8549                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8550             };
8551             
8552             this.maskEl.top.enableDisplayMode("block");
8553             this.maskEl.left.enableDisplayMode("block");
8554             this.maskEl.bottom.enableDisplayMode("block");
8555             this.maskEl.right.enableDisplayMode("block");
8556             
8557             this.toolTip = new Roo.bootstrap.Tooltip({
8558                 cls : 'roo-form-error-popover',
8559                 alignment : {
8560                     'left' : ['r-l', [-2,0], 'right'],
8561                     'right' : ['l-r', [2,0], 'left'],
8562                     'bottom' : ['tl-bl', [0,2], 'top'],
8563                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8564                 }
8565             });
8566             
8567             this.toolTip.render(Roo.get(document.body));
8568
8569             this.toolTip.el.enableDisplayMode("block");
8570             
8571             Roo.get(document.body).on('click', function(){
8572                 this.unmask();
8573             }, this);
8574             
8575             Roo.get(document.body).on('touchstart', function(){
8576                 this.unmask();
8577             }, this);
8578             
8579             this.isApplied = true
8580         },
8581         
8582         mask : function(form, target)
8583         {
8584             this.form = form;
8585             
8586             this.target = target;
8587             
8588             if(!this.form.errorMask || !target.el){
8589                 return;
8590             }
8591             
8592             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8593             
8594             Roo.log(scrollable);
8595             
8596             var ot = this.target.el.calcOffsetsTo(scrollable);
8597             
8598             var scrollTo = ot[1] - this.form.maskOffset;
8599             
8600             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8601             
8602             scrollable.scrollTo('top', scrollTo);
8603             
8604             var box = this.target.el.getBox();
8605             Roo.log(box);
8606             var zIndex = Roo.bootstrap.Modal.zIndex++;
8607
8608             
8609             this.maskEl.top.setStyle('position', 'absolute');
8610             this.maskEl.top.setStyle('z-index', zIndex);
8611             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8612             this.maskEl.top.setLeft(0);
8613             this.maskEl.top.setTop(0);
8614             this.maskEl.top.show();
8615             
8616             this.maskEl.left.setStyle('position', 'absolute');
8617             this.maskEl.left.setStyle('z-index', zIndex);
8618             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8619             this.maskEl.left.setLeft(0);
8620             this.maskEl.left.setTop(box.y - this.padding);
8621             this.maskEl.left.show();
8622
8623             this.maskEl.bottom.setStyle('position', 'absolute');
8624             this.maskEl.bottom.setStyle('z-index', zIndex);
8625             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8626             this.maskEl.bottom.setLeft(0);
8627             this.maskEl.bottom.setTop(box.bottom + this.padding);
8628             this.maskEl.bottom.show();
8629
8630             this.maskEl.right.setStyle('position', 'absolute');
8631             this.maskEl.right.setStyle('z-index', zIndex);
8632             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8633             this.maskEl.right.setLeft(box.right + this.padding);
8634             this.maskEl.right.setTop(box.y - this.padding);
8635             this.maskEl.right.show();
8636
8637             this.toolTip.bindEl = this.target.el;
8638
8639             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8640
8641             var tip = this.target.blankText;
8642
8643             if(this.target.getValue() !== '' ) {
8644                 
8645                 if (this.target.invalidText.length) {
8646                     tip = this.target.invalidText;
8647                 } else if (this.target.regexText.length){
8648                     tip = this.target.regexText;
8649                 }
8650             }
8651
8652             this.toolTip.show(tip);
8653
8654             this.intervalID = window.setInterval(function() {
8655                 Roo.bootstrap.Form.popover.unmask();
8656             }, 10000);
8657
8658             window.onwheel = function(){ return false;};
8659             
8660             (function(){ this.isMasked = true; }).defer(500, this);
8661             
8662         },
8663         
8664         unmask : function()
8665         {
8666             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8667                 return;
8668             }
8669             
8670             this.maskEl.top.setStyle('position', 'absolute');
8671             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8672             this.maskEl.top.hide();
8673
8674             this.maskEl.left.setStyle('position', 'absolute');
8675             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8676             this.maskEl.left.hide();
8677
8678             this.maskEl.bottom.setStyle('position', 'absolute');
8679             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8680             this.maskEl.bottom.hide();
8681
8682             this.maskEl.right.setStyle('position', 'absolute');
8683             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8684             this.maskEl.right.hide();
8685             
8686             this.toolTip.hide();
8687             
8688             this.toolTip.el.hide();
8689             
8690             window.onwheel = function(){ return true;};
8691             
8692             if(this.intervalID){
8693                 window.clearInterval(this.intervalID);
8694                 this.intervalID = false;
8695             }
8696             
8697             this.isMasked = false;
8698             
8699         }
8700         
8701     }
8702     
8703 });
8704
8705 /*
8706  * Based on:
8707  * Ext JS Library 1.1.1
8708  * Copyright(c) 2006-2007, Ext JS, LLC.
8709  *
8710  * Originally Released Under LGPL - original licence link has changed is not relivant.
8711  *
8712  * Fork - LGPL
8713  * <script type="text/javascript">
8714  */
8715 /**
8716  * @class Roo.form.VTypes
8717  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8718  * @singleton
8719  */
8720 Roo.form.VTypes = function(){
8721     // closure these in so they are only created once.
8722     var alpha = /^[a-zA-Z_]+$/;
8723     var alphanum = /^[a-zA-Z0-9_]+$/;
8724     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8725     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8726
8727     // All these messages and functions are configurable
8728     return {
8729         /**
8730          * The function used to validate email addresses
8731          * @param {String} value The email address
8732          */
8733         'email' : function(v){
8734             return email.test(v);
8735         },
8736         /**
8737          * The error text to display when the email validation function returns false
8738          * @type String
8739          */
8740         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8741         /**
8742          * The keystroke filter mask to be applied on email input
8743          * @type RegExp
8744          */
8745         'emailMask' : /[a-z0-9_\.\-@]/i,
8746
8747         /**
8748          * The function used to validate URLs
8749          * @param {String} value The URL
8750          */
8751         'url' : function(v){
8752             return url.test(v);
8753         },
8754         /**
8755          * The error text to display when the url validation function returns false
8756          * @type String
8757          */
8758         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8759         
8760         /**
8761          * The function used to validate alpha values
8762          * @param {String} value The value
8763          */
8764         'alpha' : function(v){
8765             return alpha.test(v);
8766         },
8767         /**
8768          * The error text to display when the alpha validation function returns false
8769          * @type String
8770          */
8771         'alphaText' : 'This field should only contain letters and _',
8772         /**
8773          * The keystroke filter mask to be applied on alpha input
8774          * @type RegExp
8775          */
8776         'alphaMask' : /[a-z_]/i,
8777
8778         /**
8779          * The function used to validate alphanumeric values
8780          * @param {String} value The value
8781          */
8782         'alphanum' : function(v){
8783             return alphanum.test(v);
8784         },
8785         /**
8786          * The error text to display when the alphanumeric validation function returns false
8787          * @type String
8788          */
8789         'alphanumText' : 'This field should only contain letters, numbers and _',
8790         /**
8791          * The keystroke filter mask to be applied on alphanumeric input
8792          * @type RegExp
8793          */
8794         'alphanumMask' : /[a-z0-9_]/i
8795     };
8796 }();/*
8797  * - LGPL
8798  *
8799  * Input
8800  * 
8801  */
8802
8803 /**
8804  * @class Roo.bootstrap.Input
8805  * @extends Roo.bootstrap.Component
8806  * Bootstrap Input class
8807  * @cfg {Boolean} disabled is it disabled
8808  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8809  * @cfg {String} name name of the input
8810  * @cfg {string} fieldLabel - the label associated
8811  * @cfg {string} placeholder - placeholder to put in text.
8812  * @cfg {string}  before - input group add on before
8813  * @cfg {string} after - input group add on after
8814  * @cfg {string} size - (lg|sm) or leave empty..
8815  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8816  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8817  * @cfg {Number} md colspan out of 12 for computer-sized screens
8818  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8819  * @cfg {string} value default value of the input
8820  * @cfg {Number} labelWidth set the width of label 
8821  * @cfg {Number} labellg set the width of label (1-12)
8822  * @cfg {Number} labelmd set the width of label (1-12)
8823  * @cfg {Number} labelsm set the width of label (1-12)
8824  * @cfg {Number} labelxs set the width of label (1-12)
8825  * @cfg {String} labelAlign (top|left)
8826  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8827  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8828  * @cfg {String} indicatorpos (left|right) default left
8829  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8830  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8831
8832  * @cfg {String} align (left|center|right) Default left
8833  * @cfg {Boolean} forceFeedback (true|false) Default false
8834  * 
8835  * @constructor
8836  * Create a new Input
8837  * @param {Object} config The config object
8838  */
8839
8840 Roo.bootstrap.Input = function(config){
8841     
8842     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8843     
8844     this.addEvents({
8845         /**
8846          * @event focus
8847          * Fires when this field receives input focus.
8848          * @param {Roo.form.Field} this
8849          */
8850         focus : true,
8851         /**
8852          * @event blur
8853          * Fires when this field loses input focus.
8854          * @param {Roo.form.Field} this
8855          */
8856         blur : true,
8857         /**
8858          * @event specialkey
8859          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8860          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8861          * @param {Roo.form.Field} this
8862          * @param {Roo.EventObject} e The event object
8863          */
8864         specialkey : true,
8865         /**
8866          * @event change
8867          * Fires just before the field blurs if the field value has changed.
8868          * @param {Roo.form.Field} this
8869          * @param {Mixed} newValue The new value
8870          * @param {Mixed} oldValue The original value
8871          */
8872         change : true,
8873         /**
8874          * @event invalid
8875          * Fires after the field has been marked as invalid.
8876          * @param {Roo.form.Field} this
8877          * @param {String} msg The validation message
8878          */
8879         invalid : true,
8880         /**
8881          * @event valid
8882          * Fires after the field has been validated with no errors.
8883          * @param {Roo.form.Field} this
8884          */
8885         valid : true,
8886          /**
8887          * @event keyup
8888          * Fires after the key up
8889          * @param {Roo.form.Field} this
8890          * @param {Roo.EventObject}  e The event Object
8891          */
8892         keyup : true
8893     });
8894 };
8895
8896 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8897      /**
8898      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8899       automatic validation (defaults to "keyup").
8900      */
8901     validationEvent : "keyup",
8902      /**
8903      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8904      */
8905     validateOnBlur : true,
8906     /**
8907      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8908      */
8909     validationDelay : 250,
8910      /**
8911      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8912      */
8913     focusClass : "x-form-focus",  // not needed???
8914     
8915        
8916     /**
8917      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8918      */
8919     invalidClass : "has-warning",
8920     
8921     /**
8922      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8923      */
8924     validClass : "has-success",
8925     
8926     /**
8927      * @cfg {Boolean} hasFeedback (true|false) default true
8928      */
8929     hasFeedback : true,
8930     
8931     /**
8932      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8933      */
8934     invalidFeedbackClass : "glyphicon-warning-sign",
8935     
8936     /**
8937      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8938      */
8939     validFeedbackClass : "glyphicon-ok",
8940     
8941     /**
8942      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8943      */
8944     selectOnFocus : false,
8945     
8946      /**
8947      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8948      */
8949     maskRe : null,
8950        /**
8951      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8952      */
8953     vtype : null,
8954     
8955       /**
8956      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8957      */
8958     disableKeyFilter : false,
8959     
8960        /**
8961      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8962      */
8963     disabled : false,
8964      /**
8965      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8966      */
8967     allowBlank : true,
8968     /**
8969      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8970      */
8971     blankText : "Please complete this mandatory field",
8972     
8973      /**
8974      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8975      */
8976     minLength : 0,
8977     /**
8978      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8979      */
8980     maxLength : Number.MAX_VALUE,
8981     /**
8982      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8983      */
8984     minLengthText : "The minimum length for this field is {0}",
8985     /**
8986      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8987      */
8988     maxLengthText : "The maximum length for this field is {0}",
8989   
8990     
8991     /**
8992      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8993      * If available, this function will be called only after the basic validators all return true, and will be passed the
8994      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8995      */
8996     validator : null,
8997     /**
8998      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8999      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9000      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9001      */
9002     regex : null,
9003     /**
9004      * @cfg {String} regexText -- Depricated - use Invalid Text
9005      */
9006     regexText : "",
9007     
9008     /**
9009      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9010      */
9011     invalidText : "",
9012     
9013     
9014     
9015     autocomplete: false,
9016     
9017     
9018     fieldLabel : '',
9019     inputType : 'text',
9020     
9021     name : false,
9022     placeholder: false,
9023     before : false,
9024     after : false,
9025     size : false,
9026     hasFocus : false,
9027     preventMark: false,
9028     isFormField : true,
9029     value : '',
9030     labelWidth : 2,
9031     labelAlign : false,
9032     readOnly : false,
9033     align : false,
9034     formatedValue : false,
9035     forceFeedback : false,
9036     
9037     indicatorpos : 'left',
9038     
9039     labellg : 0,
9040     labelmd : 0,
9041     labelsm : 0,
9042     labelxs : 0,
9043     
9044     capture : '',
9045     accept : '',
9046     
9047     parentLabelAlign : function()
9048     {
9049         var parent = this;
9050         while (parent.parent()) {
9051             parent = parent.parent();
9052             if (typeof(parent.labelAlign) !='undefined') {
9053                 return parent.labelAlign;
9054             }
9055         }
9056         return 'left';
9057         
9058     },
9059     
9060     getAutoCreate : function()
9061     {
9062         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9063         
9064         var id = Roo.id();
9065         
9066         var cfg = {};
9067         
9068         if(this.inputType != 'hidden'){
9069             cfg.cls = 'form-group' //input-group
9070         }
9071         
9072         var input =  {
9073             tag: 'input',
9074             id : id,
9075             type : this.inputType,
9076             value : this.value,
9077             cls : 'form-control',
9078             placeholder : this.placeholder || '',
9079             autocomplete : this.autocomplete || 'new-password'
9080         };
9081         
9082         if(this.capture.length){
9083             input.capture = this.capture;
9084         }
9085         
9086         if(this.accept.length){
9087             input.accept = this.accept + "/*";
9088         }
9089         
9090         if(this.align){
9091             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9092         }
9093         
9094         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9095             input.maxLength = this.maxLength;
9096         }
9097         
9098         if (this.disabled) {
9099             input.disabled=true;
9100         }
9101         
9102         if (this.readOnly) {
9103             input.readonly=true;
9104         }
9105         
9106         if (this.name) {
9107             input.name = this.name;
9108         }
9109         
9110         if (this.size) {
9111             input.cls += ' input-' + this.size;
9112         }
9113         
9114         var settings=this;
9115         ['xs','sm','md','lg'].map(function(size){
9116             if (settings[size]) {
9117                 cfg.cls += ' col-' + size + '-' + settings[size];
9118             }
9119         });
9120         
9121         var inputblock = input;
9122         
9123         var feedback = {
9124             tag: 'span',
9125             cls: 'glyphicon form-control-feedback'
9126         };
9127             
9128         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9129             
9130             inputblock = {
9131                 cls : 'has-feedback',
9132                 cn :  [
9133                     input,
9134                     feedback
9135                 ] 
9136             };  
9137         }
9138         
9139         if (this.before || this.after) {
9140             
9141             inputblock = {
9142                 cls : 'input-group',
9143                 cn :  [] 
9144             };
9145             
9146             if (this.before && typeof(this.before) == 'string') {
9147                 
9148                 inputblock.cn.push({
9149                     tag :'span',
9150                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9151                     html : this.before
9152                 });
9153             }
9154             if (this.before && typeof(this.before) == 'object') {
9155                 this.before = Roo.factory(this.before);
9156                 
9157                 inputblock.cn.push({
9158                     tag :'span',
9159                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9160                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9161                 });
9162             }
9163             
9164             inputblock.cn.push(input);
9165             
9166             if (this.after && typeof(this.after) == 'string') {
9167                 inputblock.cn.push({
9168                     tag :'span',
9169                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9170                     html : this.after
9171                 });
9172             }
9173             if (this.after && typeof(this.after) == 'object') {
9174                 this.after = Roo.factory(this.after);
9175                 
9176                 inputblock.cn.push({
9177                     tag :'span',
9178                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9179                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9180                 });
9181             }
9182             
9183             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9184                 inputblock.cls += ' has-feedback';
9185                 inputblock.cn.push(feedback);
9186             }
9187         };
9188         var indicator = {
9189             tag : 'i',
9190             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9191             tooltip : 'This field is required'
9192         };
9193         if (Roo.bootstrap.version == 4) {
9194             indicator = {
9195                 tag : 'i',
9196                 style : 'display-none'
9197             };
9198         }
9199         if (align ==='left' && this.fieldLabel.length) {
9200             
9201             cfg.cls += ' roo-form-group-label-left row';
9202             
9203             cfg.cn = [
9204                 indicator,
9205                 {
9206                     tag: 'label',
9207                     'for' :  id,
9208                     cls : 'control-label col-form-label',
9209                     html : this.fieldLabel
9210
9211                 },
9212                 {
9213                     cls : "", 
9214                     cn: [
9215                         inputblock
9216                     ]
9217                 }
9218             ];
9219             
9220             var labelCfg = cfg.cn[1];
9221             var contentCfg = cfg.cn[2];
9222             
9223             if(this.indicatorpos == 'right'){
9224                 cfg.cn = [
9225                     {
9226                         tag: 'label',
9227                         'for' :  id,
9228                         cls : 'control-label col-form-label',
9229                         cn : [
9230                             {
9231                                 tag : 'span',
9232                                 html : this.fieldLabel
9233                             },
9234                             indicator
9235                         ]
9236                     },
9237                     {
9238                         cls : "",
9239                         cn: [
9240                             inputblock
9241                         ]
9242                     }
9243
9244                 ];
9245                 
9246                 labelCfg = cfg.cn[0];
9247                 contentCfg = cfg.cn[1];
9248             
9249             }
9250             
9251             if(this.labelWidth > 12){
9252                 labelCfg.style = "width: " + this.labelWidth + 'px';
9253             }
9254             
9255             if(this.labelWidth < 13 && this.labelmd == 0){
9256                 this.labelmd = this.labelWidth;
9257             }
9258             
9259             if(this.labellg > 0){
9260                 labelCfg.cls += ' col-lg-' + this.labellg;
9261                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9262             }
9263             
9264             if(this.labelmd > 0){
9265                 labelCfg.cls += ' col-md-' + this.labelmd;
9266                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9267             }
9268             
9269             if(this.labelsm > 0){
9270                 labelCfg.cls += ' col-sm-' + this.labelsm;
9271                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9272             }
9273             
9274             if(this.labelxs > 0){
9275                 labelCfg.cls += ' col-xs-' + this.labelxs;
9276                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9277             }
9278             
9279             
9280         } else if ( this.fieldLabel.length) {
9281                 
9282             cfg.cn = [
9283                 {
9284                     tag : 'i',
9285                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9286                     tooltip : 'This field is required'
9287                 },
9288                 {
9289                     tag: 'label',
9290                    //cls : 'input-group-addon',
9291                     html : this.fieldLabel
9292
9293                 },
9294
9295                inputblock
9296
9297            ];
9298            
9299            if(this.indicatorpos == 'right'){
9300                 
9301                 cfg.cn = [
9302                     {
9303                         tag: 'label',
9304                        //cls : 'input-group-addon',
9305                         html : this.fieldLabel
9306
9307                     },
9308                     {
9309                         tag : 'i',
9310                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9311                         tooltip : 'This field is required'
9312                     },
9313
9314                    inputblock
9315
9316                ];
9317
9318             }
9319
9320         } else {
9321             
9322             cfg.cn = [
9323
9324                     inputblock
9325
9326             ];
9327                 
9328                 
9329         };
9330         
9331         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9332            cfg.cls += ' navbar-form';
9333         }
9334         
9335         if (this.parentType === 'NavGroup') {
9336            cfg.cls += ' navbar-form';
9337            cfg.tag = 'li';
9338         }
9339         
9340         return cfg;
9341         
9342     },
9343     /**
9344      * return the real input element.
9345      */
9346     inputEl: function ()
9347     {
9348         return this.el.select('input.form-control',true).first();
9349     },
9350     
9351     tooltipEl : function()
9352     {
9353         return this.inputEl();
9354     },
9355     
9356     indicatorEl : function()
9357     {
9358         if (Roo.bootstrap.version == 4) {
9359             return false; // not enabled in v4 yet.
9360         }
9361         
9362         var indicator = this.el.select('i.roo-required-indicator',true).first();
9363         
9364         if(!indicator){
9365             return false;
9366         }
9367         
9368         return indicator;
9369         
9370     },
9371     
9372     setDisabled : function(v)
9373     {
9374         var i  = this.inputEl().dom;
9375         if (!v) {
9376             i.removeAttribute('disabled');
9377             return;
9378             
9379         }
9380         i.setAttribute('disabled','true');
9381     },
9382     initEvents : function()
9383     {
9384           
9385         this.inputEl().on("keydown" , this.fireKey,  this);
9386         this.inputEl().on("focus", this.onFocus,  this);
9387         this.inputEl().on("blur", this.onBlur,  this);
9388         
9389         this.inputEl().relayEvent('keyup', this);
9390         
9391         this.indicator = this.indicatorEl();
9392         
9393         if(this.indicator){
9394             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9395         }
9396  
9397         // reference to original value for reset
9398         this.originalValue = this.getValue();
9399         //Roo.form.TextField.superclass.initEvents.call(this);
9400         if(this.validationEvent == 'keyup'){
9401             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9402             this.inputEl().on('keyup', this.filterValidation, this);
9403         }
9404         else if(this.validationEvent !== false){
9405             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9406         }
9407         
9408         if(this.selectOnFocus){
9409             this.on("focus", this.preFocus, this);
9410             
9411         }
9412         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9413             this.inputEl().on("keypress", this.filterKeys, this);
9414         } else {
9415             this.inputEl().relayEvent('keypress', this);
9416         }
9417        /* if(this.grow){
9418             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9419             this.el.on("click", this.autoSize,  this);
9420         }
9421         */
9422         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9423             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9424         }
9425         
9426         if (typeof(this.before) == 'object') {
9427             this.before.render(this.el.select('.roo-input-before',true).first());
9428         }
9429         if (typeof(this.after) == 'object') {
9430             this.after.render(this.el.select('.roo-input-after',true).first());
9431         }
9432         
9433         this.inputEl().on('change', this.onChange, this);
9434         
9435     },
9436     filterValidation : function(e){
9437         if(!e.isNavKeyPress()){
9438             this.validationTask.delay(this.validationDelay);
9439         }
9440     },
9441      /**
9442      * Validates the field value
9443      * @return {Boolean} True if the value is valid, else false
9444      */
9445     validate : function(){
9446         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9447         if(this.disabled || this.validateValue(this.getRawValue())){
9448             this.markValid();
9449             return true;
9450         }
9451         
9452         this.markInvalid();
9453         return false;
9454     },
9455     
9456     
9457     /**
9458      * Validates a value according to the field's validation rules and marks the field as invalid
9459      * if the validation fails
9460      * @param {Mixed} value The value to validate
9461      * @return {Boolean} True if the value is valid, else false
9462      */
9463     validateValue : function(value)
9464     {
9465         if(this.getVisibilityEl().hasClass('hidden')){
9466             return true;
9467         }
9468         
9469         if(value.length < 1)  { // if it's blank
9470             if(this.allowBlank){
9471                 return true;
9472             }
9473             return false;
9474         }
9475         
9476         if(value.length < this.minLength){
9477             return false;
9478         }
9479         if(value.length > this.maxLength){
9480             return false;
9481         }
9482         if(this.vtype){
9483             var vt = Roo.form.VTypes;
9484             if(!vt[this.vtype](value, this)){
9485                 return false;
9486             }
9487         }
9488         if(typeof this.validator == "function"){
9489             var msg = this.validator(value);
9490             if(msg !== true){
9491                 return false;
9492             }
9493             if (typeof(msg) == 'string') {
9494                 this.invalidText = msg;
9495             }
9496         }
9497         
9498         if(this.regex && !this.regex.test(value)){
9499             return false;
9500         }
9501         
9502         return true;
9503     },
9504     
9505      // private
9506     fireKey : function(e){
9507         //Roo.log('field ' + e.getKey());
9508         if(e.isNavKeyPress()){
9509             this.fireEvent("specialkey", this, e);
9510         }
9511     },
9512     focus : function (selectText){
9513         if(this.rendered){
9514             this.inputEl().focus();
9515             if(selectText === true){
9516                 this.inputEl().dom.select();
9517             }
9518         }
9519         return this;
9520     } ,
9521     
9522     onFocus : function(){
9523         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9524            // this.el.addClass(this.focusClass);
9525         }
9526         if(!this.hasFocus){
9527             this.hasFocus = true;
9528             this.startValue = this.getValue();
9529             this.fireEvent("focus", this);
9530         }
9531     },
9532     
9533     beforeBlur : Roo.emptyFn,
9534
9535     
9536     // private
9537     onBlur : function(){
9538         this.beforeBlur();
9539         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9540             //this.el.removeClass(this.focusClass);
9541         }
9542         this.hasFocus = false;
9543         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9544             this.validate();
9545         }
9546         var v = this.getValue();
9547         if(String(v) !== String(this.startValue)){
9548             this.fireEvent('change', this, v, this.startValue);
9549         }
9550         this.fireEvent("blur", this);
9551     },
9552     
9553     onChange : function(e)
9554     {
9555         var v = this.getValue();
9556         if(String(v) !== String(this.startValue)){
9557             this.fireEvent('change', this, v, this.startValue);
9558         }
9559         
9560     },
9561     
9562     /**
9563      * Resets the current field value to the originally loaded value and clears any validation messages
9564      */
9565     reset : function(){
9566         this.setValue(this.originalValue);
9567         this.validate();
9568     },
9569      /**
9570      * Returns the name of the field
9571      * @return {Mixed} name The name field
9572      */
9573     getName: function(){
9574         return this.name;
9575     },
9576      /**
9577      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9578      * @return {Mixed} value The field value
9579      */
9580     getValue : function(){
9581         
9582         var v = this.inputEl().getValue();
9583         
9584         return v;
9585     },
9586     /**
9587      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9588      * @return {Mixed} value The field value
9589      */
9590     getRawValue : function(){
9591         var v = this.inputEl().getValue();
9592         
9593         return v;
9594     },
9595     
9596     /**
9597      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9598      * @param {Mixed} value The value to set
9599      */
9600     setRawValue : function(v){
9601         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9602     },
9603     
9604     selectText : function(start, end){
9605         var v = this.getRawValue();
9606         if(v.length > 0){
9607             start = start === undefined ? 0 : start;
9608             end = end === undefined ? v.length : end;
9609             var d = this.inputEl().dom;
9610             if(d.setSelectionRange){
9611                 d.setSelectionRange(start, end);
9612             }else if(d.createTextRange){
9613                 var range = d.createTextRange();
9614                 range.moveStart("character", start);
9615                 range.moveEnd("character", v.length-end);
9616                 range.select();
9617             }
9618         }
9619     },
9620     
9621     /**
9622      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9623      * @param {Mixed} value The value to set
9624      */
9625     setValue : function(v){
9626         this.value = v;
9627         if(this.rendered){
9628             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9629             this.validate();
9630         }
9631     },
9632     
9633     /*
9634     processValue : function(value){
9635         if(this.stripCharsRe){
9636             var newValue = value.replace(this.stripCharsRe, '');
9637             if(newValue !== value){
9638                 this.setRawValue(newValue);
9639                 return newValue;
9640             }
9641         }
9642         return value;
9643     },
9644   */
9645     preFocus : function(){
9646         
9647         if(this.selectOnFocus){
9648             this.inputEl().dom.select();
9649         }
9650     },
9651     filterKeys : function(e){
9652         var k = e.getKey();
9653         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9654             return;
9655         }
9656         var c = e.getCharCode(), cc = String.fromCharCode(c);
9657         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9658             return;
9659         }
9660         if(!this.maskRe.test(cc)){
9661             e.stopEvent();
9662         }
9663     },
9664      /**
9665      * Clear any invalid styles/messages for this field
9666      */
9667     clearInvalid : function(){
9668         
9669         if(!this.el || this.preventMark){ // not rendered
9670             return;
9671         }
9672         
9673      
9674         this.el.removeClass(this.invalidClass);
9675         
9676         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9677             
9678             var feedback = this.el.select('.form-control-feedback', true).first();
9679             
9680             if(feedback){
9681                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9682             }
9683             
9684         }
9685         
9686         if(this.indicator){
9687             this.indicator.removeClass('visible');
9688             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9689         }
9690         
9691         this.fireEvent('valid', this);
9692     },
9693     
9694      /**
9695      * Mark this field as valid
9696      */
9697     markValid : function()
9698     {
9699         if(!this.el  || this.preventMark){ // not rendered...
9700             return;
9701         }
9702         
9703         this.el.removeClass([this.invalidClass, this.validClass]);
9704         
9705         var feedback = this.el.select('.form-control-feedback', true).first();
9706             
9707         if(feedback){
9708             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9709         }
9710         
9711         if(this.indicator){
9712             this.indicator.removeClass('visible');
9713             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9714         }
9715         
9716         if(this.disabled){
9717             return;
9718         }
9719         
9720         if(this.allowBlank && !this.getRawValue().length){
9721             return;
9722         }
9723         
9724         this.el.addClass(this.validClass);
9725         
9726         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9727             
9728             var feedback = this.el.select('.form-control-feedback', true).first();
9729             
9730             if(feedback){
9731                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9732                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9733             }
9734             
9735         }
9736         
9737         this.fireEvent('valid', this);
9738     },
9739     
9740      /**
9741      * Mark this field as invalid
9742      * @param {String} msg The validation message
9743      */
9744     markInvalid : function(msg)
9745     {
9746         if(!this.el  || this.preventMark){ // not rendered
9747             return;
9748         }
9749         
9750         this.el.removeClass([this.invalidClass, this.validClass]);
9751         
9752         var feedback = this.el.select('.form-control-feedback', true).first();
9753             
9754         if(feedback){
9755             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9756         }
9757
9758         if(this.disabled){
9759             return;
9760         }
9761         
9762         if(this.allowBlank && !this.getRawValue().length){
9763             return;
9764         }
9765         
9766         if(this.indicator){
9767             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9768             this.indicator.addClass('visible');
9769         }
9770         
9771         this.el.addClass(this.invalidClass);
9772         
9773         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9774             
9775             var feedback = this.el.select('.form-control-feedback', true).first();
9776             
9777             if(feedback){
9778                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9779                 
9780                 if(this.getValue().length || this.forceFeedback){
9781                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9782                 }
9783                 
9784             }
9785             
9786         }
9787         
9788         this.fireEvent('invalid', this, msg);
9789     },
9790     // private
9791     SafariOnKeyDown : function(event)
9792     {
9793         // this is a workaround for a password hang bug on chrome/ webkit.
9794         if (this.inputEl().dom.type != 'password') {
9795             return;
9796         }
9797         
9798         var isSelectAll = false;
9799         
9800         if(this.inputEl().dom.selectionEnd > 0){
9801             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9802         }
9803         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9804             event.preventDefault();
9805             this.setValue('');
9806             return;
9807         }
9808         
9809         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9810             
9811             event.preventDefault();
9812             // this is very hacky as keydown always get's upper case.
9813             //
9814             var cc = String.fromCharCode(event.getCharCode());
9815             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9816             
9817         }
9818     },
9819     adjustWidth : function(tag, w){
9820         tag = tag.toLowerCase();
9821         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9822             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9823                 if(tag == 'input'){
9824                     return w + 2;
9825                 }
9826                 if(tag == 'textarea'){
9827                     return w-2;
9828                 }
9829             }else if(Roo.isOpera){
9830                 if(tag == 'input'){
9831                     return w + 2;
9832                 }
9833                 if(tag == 'textarea'){
9834                     return w-2;
9835                 }
9836             }
9837         }
9838         return w;
9839     },
9840     
9841     setFieldLabel : function(v)
9842     {
9843         if(!this.rendered){
9844             return;
9845         }
9846         
9847         if(this.indicatorEl()){
9848             var ar = this.el.select('label > span',true);
9849             
9850             if (ar.elements.length) {
9851                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9852                 this.fieldLabel = v;
9853                 return;
9854             }
9855             
9856             var br = this.el.select('label',true);
9857             
9858             if(br.elements.length) {
9859                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9860                 this.fieldLabel = v;
9861                 return;
9862             }
9863             
9864             Roo.log('Cannot Found any of label > span || label in input');
9865             return;
9866         }
9867         
9868         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9869         this.fieldLabel = v;
9870         
9871         
9872     }
9873 });
9874
9875  
9876 /*
9877  * - LGPL
9878  *
9879  * Input
9880  * 
9881  */
9882
9883 /**
9884  * @class Roo.bootstrap.TextArea
9885  * @extends Roo.bootstrap.Input
9886  * Bootstrap TextArea class
9887  * @cfg {Number} cols Specifies the visible width of a text area
9888  * @cfg {Number} rows Specifies the visible number of lines in a text area
9889  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9890  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9891  * @cfg {string} html text
9892  * 
9893  * @constructor
9894  * Create a new TextArea
9895  * @param {Object} config The config object
9896  */
9897
9898 Roo.bootstrap.TextArea = function(config){
9899     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9900    
9901 };
9902
9903 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9904      
9905     cols : false,
9906     rows : 5,
9907     readOnly : false,
9908     warp : 'soft',
9909     resize : false,
9910     value: false,
9911     html: false,
9912     
9913     getAutoCreate : function(){
9914         
9915         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9916         
9917         var id = Roo.id();
9918         
9919         var cfg = {};
9920         
9921         if(this.inputType != 'hidden'){
9922             cfg.cls = 'form-group' //input-group
9923         }
9924         
9925         var input =  {
9926             tag: 'textarea',
9927             id : id,
9928             warp : this.warp,
9929             rows : this.rows,
9930             value : this.value || '',
9931             html: this.html || '',
9932             cls : 'form-control',
9933             placeholder : this.placeholder || '' 
9934             
9935         };
9936         
9937         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9938             input.maxLength = this.maxLength;
9939         }
9940         
9941         if(this.resize){
9942             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9943         }
9944         
9945         if(this.cols){
9946             input.cols = this.cols;
9947         }
9948         
9949         if (this.readOnly) {
9950             input.readonly = true;
9951         }
9952         
9953         if (this.name) {
9954             input.name = this.name;
9955         }
9956         
9957         if (this.size) {
9958             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9959         }
9960         
9961         var settings=this;
9962         ['xs','sm','md','lg'].map(function(size){
9963             if (settings[size]) {
9964                 cfg.cls += ' col-' + size + '-' + settings[size];
9965             }
9966         });
9967         
9968         var inputblock = input;
9969         
9970         if(this.hasFeedback && !this.allowBlank){
9971             
9972             var feedback = {
9973                 tag: 'span',
9974                 cls: 'glyphicon form-control-feedback'
9975             };
9976
9977             inputblock = {
9978                 cls : 'has-feedback',
9979                 cn :  [
9980                     input,
9981                     feedback
9982                 ] 
9983             };  
9984         }
9985         
9986         
9987         if (this.before || this.after) {
9988             
9989             inputblock = {
9990                 cls : 'input-group',
9991                 cn :  [] 
9992             };
9993             if (this.before) {
9994                 inputblock.cn.push({
9995                     tag :'span',
9996                     cls : 'input-group-addon',
9997                     html : this.before
9998                 });
9999             }
10000             
10001             inputblock.cn.push(input);
10002             
10003             if(this.hasFeedback && !this.allowBlank){
10004                 inputblock.cls += ' has-feedback';
10005                 inputblock.cn.push(feedback);
10006             }
10007             
10008             if (this.after) {
10009                 inputblock.cn.push({
10010                     tag :'span',
10011                     cls : 'input-group-addon',
10012                     html : this.after
10013                 });
10014             }
10015             
10016         }
10017         
10018         if (align ==='left' && this.fieldLabel.length) {
10019             cfg.cn = [
10020                 {
10021                     tag: 'label',
10022                     'for' :  id,
10023                     cls : 'control-label',
10024                     html : this.fieldLabel
10025                 },
10026                 {
10027                     cls : "",
10028                     cn: [
10029                         inputblock
10030                     ]
10031                 }
10032
10033             ];
10034             
10035             if(this.labelWidth > 12){
10036                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10037             }
10038
10039             if(this.labelWidth < 13 && this.labelmd == 0){
10040                 this.labelmd = this.labelWidth;
10041             }
10042
10043             if(this.labellg > 0){
10044                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10045                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10046             }
10047
10048             if(this.labelmd > 0){
10049                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10050                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10051             }
10052
10053             if(this.labelsm > 0){
10054                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10055                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10056             }
10057
10058             if(this.labelxs > 0){
10059                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10060                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10061             }
10062             
10063         } else if ( this.fieldLabel.length) {
10064             cfg.cn = [
10065
10066                {
10067                    tag: 'label',
10068                    //cls : 'input-group-addon',
10069                    html : this.fieldLabel
10070
10071                },
10072
10073                inputblock
10074
10075            ];
10076
10077         } else {
10078
10079             cfg.cn = [
10080
10081                 inputblock
10082
10083             ];
10084                 
10085         }
10086         
10087         if (this.disabled) {
10088             input.disabled=true;
10089         }
10090         
10091         return cfg;
10092         
10093     },
10094     /**
10095      * return the real textarea element.
10096      */
10097     inputEl: function ()
10098     {
10099         return this.el.select('textarea.form-control',true).first();
10100     },
10101     
10102     /**
10103      * Clear any invalid styles/messages for this field
10104      */
10105     clearInvalid : function()
10106     {
10107         
10108         if(!this.el || this.preventMark){ // not rendered
10109             return;
10110         }
10111         
10112         var label = this.el.select('label', true).first();
10113         var icon = this.el.select('i.fa-star', true).first();
10114         
10115         if(label && icon){
10116             icon.remove();
10117         }
10118         
10119         this.el.removeClass(this.invalidClass);
10120         
10121         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10122             
10123             var feedback = this.el.select('.form-control-feedback', true).first();
10124             
10125             if(feedback){
10126                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10127             }
10128             
10129         }
10130         
10131         this.fireEvent('valid', this);
10132     },
10133     
10134      /**
10135      * Mark this field as valid
10136      */
10137     markValid : function()
10138     {
10139         if(!this.el  || this.preventMark){ // not rendered
10140             return;
10141         }
10142         
10143         this.el.removeClass([this.invalidClass, this.validClass]);
10144         
10145         var feedback = this.el.select('.form-control-feedback', true).first();
10146             
10147         if(feedback){
10148             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10149         }
10150
10151         if(this.disabled || this.allowBlank){
10152             return;
10153         }
10154         
10155         var label = this.el.select('label', true).first();
10156         var icon = this.el.select('i.fa-star', true).first();
10157         
10158         if(label && icon){
10159             icon.remove();
10160         }
10161         
10162         this.el.addClass(this.validClass);
10163         
10164         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10165             
10166             var feedback = this.el.select('.form-control-feedback', true).first();
10167             
10168             if(feedback){
10169                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10170                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10171             }
10172             
10173         }
10174         
10175         this.fireEvent('valid', this);
10176     },
10177     
10178      /**
10179      * Mark this field as invalid
10180      * @param {String} msg The validation message
10181      */
10182     markInvalid : function(msg)
10183     {
10184         if(!this.el  || this.preventMark){ // not rendered
10185             return;
10186         }
10187         
10188         this.el.removeClass([this.invalidClass, this.validClass]);
10189         
10190         var feedback = this.el.select('.form-control-feedback', true).first();
10191             
10192         if(feedback){
10193             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10194         }
10195
10196         if(this.disabled || this.allowBlank){
10197             return;
10198         }
10199         
10200         var label = this.el.select('label', true).first();
10201         var icon = this.el.select('i.fa-star', true).first();
10202         
10203         if(!this.getValue().length && label && !icon){
10204             this.el.createChild({
10205                 tag : 'i',
10206                 cls : 'text-danger fa fa-lg fa-star',
10207                 tooltip : 'This field is required',
10208                 style : 'margin-right:5px;'
10209             }, label, true);
10210         }
10211
10212         this.el.addClass(this.invalidClass);
10213         
10214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10215             
10216             var feedback = this.el.select('.form-control-feedback', true).first();
10217             
10218             if(feedback){
10219                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10220                 
10221                 if(this.getValue().length || this.forceFeedback){
10222                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10223                 }
10224                 
10225             }
10226             
10227         }
10228         
10229         this.fireEvent('invalid', this, msg);
10230     }
10231 });
10232
10233  
10234 /*
10235  * - LGPL
10236  *
10237  * trigger field - base class for combo..
10238  * 
10239  */
10240  
10241 /**
10242  * @class Roo.bootstrap.TriggerField
10243  * @extends Roo.bootstrap.Input
10244  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10245  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10246  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10247  * for which you can provide a custom implementation.  For example:
10248  * <pre><code>
10249 var trigger = new Roo.bootstrap.TriggerField();
10250 trigger.onTriggerClick = myTriggerFn;
10251 trigger.applyTo('my-field');
10252 </code></pre>
10253  *
10254  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10255  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10256  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10257  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10258  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10259
10260  * @constructor
10261  * Create a new TriggerField.
10262  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10263  * to the base TextField)
10264  */
10265 Roo.bootstrap.TriggerField = function(config){
10266     this.mimicing = false;
10267     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10268 };
10269
10270 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10271     /**
10272      * @cfg {String} triggerClass A CSS class to apply to the trigger
10273      */
10274      /**
10275      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10276      */
10277     hideTrigger:false,
10278
10279     /**
10280      * @cfg {Boolean} removable (true|false) special filter default false
10281      */
10282     removable : false,
10283     
10284     /** @cfg {Boolean} grow @hide */
10285     /** @cfg {Number} growMin @hide */
10286     /** @cfg {Number} growMax @hide */
10287
10288     /**
10289      * @hide 
10290      * @method
10291      */
10292     autoSize: Roo.emptyFn,
10293     // private
10294     monitorTab : true,
10295     // private
10296     deferHeight : true,
10297
10298     
10299     actionMode : 'wrap',
10300     
10301     caret : false,
10302     
10303     
10304     getAutoCreate : function(){
10305        
10306         var align = this.labelAlign || this.parentLabelAlign();
10307         
10308         var id = Roo.id();
10309         
10310         var cfg = {
10311             cls: 'form-group' //input-group
10312         };
10313         
10314         
10315         var input =  {
10316             tag: 'input',
10317             id : id,
10318             type : this.inputType,
10319             cls : 'form-control',
10320             autocomplete: 'new-password',
10321             placeholder : this.placeholder || '' 
10322             
10323         };
10324         if (this.name) {
10325             input.name = this.name;
10326         }
10327         if (this.size) {
10328             input.cls += ' input-' + this.size;
10329         }
10330         
10331         if (this.disabled) {
10332             input.disabled=true;
10333         }
10334         
10335         var inputblock = input;
10336         
10337         if(this.hasFeedback && !this.allowBlank){
10338             
10339             var feedback = {
10340                 tag: 'span',
10341                 cls: 'glyphicon form-control-feedback'
10342             };
10343             
10344             if(this.removable && !this.editable && !this.tickable){
10345                 inputblock = {
10346                     cls : 'has-feedback',
10347                     cn :  [
10348                         inputblock,
10349                         {
10350                             tag: 'button',
10351                             html : 'x',
10352                             cls : 'roo-combo-removable-btn close'
10353                         },
10354                         feedback
10355                     ] 
10356                 };
10357             } else {
10358                 inputblock = {
10359                     cls : 'has-feedback',
10360                     cn :  [
10361                         inputblock,
10362                         feedback
10363                     ] 
10364                 };
10365             }
10366
10367         } else {
10368             if(this.removable && !this.editable && !this.tickable){
10369                 inputblock = {
10370                     cls : 'roo-removable',
10371                     cn :  [
10372                         inputblock,
10373                         {
10374                             tag: 'button',
10375                             html : 'x',
10376                             cls : 'roo-combo-removable-btn close'
10377                         }
10378                     ] 
10379                 };
10380             }
10381         }
10382         
10383         if (this.before || this.after) {
10384             
10385             inputblock = {
10386                 cls : 'input-group',
10387                 cn :  [] 
10388             };
10389             if (this.before) {
10390                 inputblock.cn.push({
10391                     tag :'span',
10392                     cls : 'input-group-addon input-group-prepend input-group-text',
10393                     html : this.before
10394                 });
10395             }
10396             
10397             inputblock.cn.push(input);
10398             
10399             if(this.hasFeedback && !this.allowBlank){
10400                 inputblock.cls += ' has-feedback';
10401                 inputblock.cn.push(feedback);
10402             }
10403             
10404             if (this.after) {
10405                 inputblock.cn.push({
10406                     tag :'span',
10407                     cls : 'input-group-addon input-group-append input-group-text',
10408                     html : this.after
10409                 });
10410             }
10411             
10412         };
10413         
10414       
10415         
10416         var ibwrap = inputblock;
10417         
10418         if(this.multiple){
10419             ibwrap = {
10420                 tag: 'ul',
10421                 cls: 'roo-select2-choices',
10422                 cn:[
10423                     {
10424                         tag: 'li',
10425                         cls: 'roo-select2-search-field',
10426                         cn: [
10427
10428                             inputblock
10429                         ]
10430                     }
10431                 ]
10432             };
10433                 
10434         }
10435         
10436         var combobox = {
10437             cls: 'roo-select2-container input-group',
10438             cn: [
10439                  {
10440                     tag: 'input',
10441                     type : 'hidden',
10442                     cls: 'form-hidden-field'
10443                 },
10444                 ibwrap
10445             ]
10446         };
10447         
10448         if(!this.multiple && this.showToggleBtn){
10449             
10450             var caret = {
10451                         tag: 'span',
10452                         cls: 'caret'
10453              };
10454             if (this.caret != false) {
10455                 caret = {
10456                      tag: 'i',
10457                      cls: 'fa fa-' + this.caret
10458                 };
10459                 
10460             }
10461             
10462             combobox.cn.push({
10463                 tag :'span',
10464                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10465                 cn : [
10466                     caret,
10467                     {
10468                         tag: 'span',
10469                         cls: 'combobox-clear',
10470                         cn  : [
10471                             {
10472                                 tag : 'i',
10473                                 cls: 'icon-remove'
10474                             }
10475                         ]
10476                     }
10477                 ]
10478
10479             })
10480         }
10481         
10482         if(this.multiple){
10483             combobox.cls += ' roo-select2-container-multi';
10484         }
10485          var indicator = {
10486             tag : 'i',
10487             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10488             tooltip : 'This field is required'
10489         };
10490         if (Roo.bootstrap.version == 4) {
10491             indicator = {
10492                 tag : 'i',
10493                 style : 'display:none'
10494             };
10495         }
10496         
10497         
10498         if (align ==='left' && this.fieldLabel.length) {
10499             
10500             cfg.cls += ' roo-form-group-label-left row';
10501
10502             cfg.cn = [
10503                 indicator,
10504                 {
10505                     tag: 'label',
10506                     'for' :  id,
10507                     cls : 'control-label',
10508                     html : this.fieldLabel
10509
10510                 },
10511                 {
10512                     cls : "", 
10513                     cn: [
10514                         combobox
10515                     ]
10516                 }
10517
10518             ];
10519             
10520             var labelCfg = cfg.cn[1];
10521             var contentCfg = cfg.cn[2];
10522             
10523             if(this.indicatorpos == 'right'){
10524                 cfg.cn = [
10525                     {
10526                         tag: 'label',
10527                         'for' :  id,
10528                         cls : 'control-label',
10529                         cn : [
10530                             {
10531                                 tag : 'span',
10532                                 html : this.fieldLabel
10533                             },
10534                             indicator
10535                         ]
10536                     },
10537                     {
10538                         cls : "", 
10539                         cn: [
10540                             combobox
10541                         ]
10542                     }
10543
10544                 ];
10545                 
10546                 labelCfg = cfg.cn[0];
10547                 contentCfg = cfg.cn[1];
10548             }
10549             
10550             if(this.labelWidth > 12){
10551                 labelCfg.style = "width: " + this.labelWidth + 'px';
10552             }
10553             
10554             if(this.labelWidth < 13 && this.labelmd == 0){
10555                 this.labelmd = this.labelWidth;
10556             }
10557             
10558             if(this.labellg > 0){
10559                 labelCfg.cls += ' col-lg-' + this.labellg;
10560                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10561             }
10562             
10563             if(this.labelmd > 0){
10564                 labelCfg.cls += ' col-md-' + this.labelmd;
10565                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10566             }
10567             
10568             if(this.labelsm > 0){
10569                 labelCfg.cls += ' col-sm-' + this.labelsm;
10570                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10571             }
10572             
10573             if(this.labelxs > 0){
10574                 labelCfg.cls += ' col-xs-' + this.labelxs;
10575                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10576             }
10577             
10578         } else if ( this.fieldLabel.length) {
10579 //                Roo.log(" label");
10580             cfg.cn = [
10581                 indicator,
10582                {
10583                    tag: 'label',
10584                    //cls : 'input-group-addon',
10585                    html : this.fieldLabel
10586
10587                },
10588
10589                combobox
10590
10591             ];
10592             
10593             if(this.indicatorpos == 'right'){
10594                 
10595                 cfg.cn = [
10596                     {
10597                        tag: 'label',
10598                        cn : [
10599                            {
10600                                tag : 'span',
10601                                html : this.fieldLabel
10602                            },
10603                            indicator
10604                        ]
10605
10606                     },
10607                     combobox
10608
10609                 ];
10610
10611             }
10612
10613         } else {
10614             
10615 //                Roo.log(" no label && no align");
10616                 cfg = combobox
10617                      
10618                 
10619         }
10620         
10621         var settings=this;
10622         ['xs','sm','md','lg'].map(function(size){
10623             if (settings[size]) {
10624                 cfg.cls += ' col-' + size + '-' + settings[size];
10625             }
10626         });
10627         
10628         return cfg;
10629         
10630     },
10631     
10632     
10633     
10634     // private
10635     onResize : function(w, h){
10636 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10637 //        if(typeof w == 'number'){
10638 //            var x = w - this.trigger.getWidth();
10639 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10640 //            this.trigger.setStyle('left', x+'px');
10641 //        }
10642     },
10643
10644     // private
10645     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10646
10647     // private
10648     getResizeEl : function(){
10649         return this.inputEl();
10650     },
10651
10652     // private
10653     getPositionEl : function(){
10654         return this.inputEl();
10655     },
10656
10657     // private
10658     alignErrorIcon : function(){
10659         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10660     },
10661
10662     // private
10663     initEvents : function(){
10664         
10665         this.createList();
10666         
10667         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10668         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10669         if(!this.multiple && this.showToggleBtn){
10670             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10671             if(this.hideTrigger){
10672                 this.trigger.setDisplayed(false);
10673             }
10674             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10675         }
10676         
10677         if(this.multiple){
10678             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10679         }
10680         
10681         if(this.removable && !this.editable && !this.tickable){
10682             var close = this.closeTriggerEl();
10683             
10684             if(close){
10685                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10686                 close.on('click', this.removeBtnClick, this, close);
10687             }
10688         }
10689         
10690         //this.trigger.addClassOnOver('x-form-trigger-over');
10691         //this.trigger.addClassOnClick('x-form-trigger-click');
10692         
10693         //if(!this.width){
10694         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10695         //}
10696     },
10697     
10698     closeTriggerEl : function()
10699     {
10700         var close = this.el.select('.roo-combo-removable-btn', true).first();
10701         return close ? close : false;
10702     },
10703     
10704     removeBtnClick : function(e, h, el)
10705     {
10706         e.preventDefault();
10707         
10708         if(this.fireEvent("remove", this) !== false){
10709             this.reset();
10710             this.fireEvent("afterremove", this)
10711         }
10712     },
10713     
10714     createList : function()
10715     {
10716         this.list = Roo.get(document.body).createChild({
10717             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10718             cls: 'typeahead typeahead-long dropdown-menu',
10719             style: 'display:none'
10720         });
10721         
10722         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10723         
10724     },
10725
10726     // private
10727     initTrigger : function(){
10728        
10729     },
10730
10731     // private
10732     onDestroy : function(){
10733         if(this.trigger){
10734             this.trigger.removeAllListeners();
10735           //  this.trigger.remove();
10736         }
10737         //if(this.wrap){
10738         //    this.wrap.remove();
10739         //}
10740         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10741     },
10742
10743     // private
10744     onFocus : function(){
10745         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10746         /*
10747         if(!this.mimicing){
10748             this.wrap.addClass('x-trigger-wrap-focus');
10749             this.mimicing = true;
10750             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10751             if(this.monitorTab){
10752                 this.el.on("keydown", this.checkTab, this);
10753             }
10754         }
10755         */
10756     },
10757
10758     // private
10759     checkTab : function(e){
10760         if(e.getKey() == e.TAB){
10761             this.triggerBlur();
10762         }
10763     },
10764
10765     // private
10766     onBlur : function(){
10767         // do nothing
10768     },
10769
10770     // private
10771     mimicBlur : function(e, t){
10772         /*
10773         if(!this.wrap.contains(t) && this.validateBlur()){
10774             this.triggerBlur();
10775         }
10776         */
10777     },
10778
10779     // private
10780     triggerBlur : function(){
10781         this.mimicing = false;
10782         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10783         if(this.monitorTab){
10784             this.el.un("keydown", this.checkTab, this);
10785         }
10786         //this.wrap.removeClass('x-trigger-wrap-focus');
10787         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10788     },
10789
10790     // private
10791     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10792     validateBlur : function(e, t){
10793         return true;
10794     },
10795
10796     // private
10797     onDisable : function(){
10798         this.inputEl().dom.disabled = true;
10799         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10800         //if(this.wrap){
10801         //    this.wrap.addClass('x-item-disabled');
10802         //}
10803     },
10804
10805     // private
10806     onEnable : function(){
10807         this.inputEl().dom.disabled = false;
10808         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10809         //if(this.wrap){
10810         //    this.el.removeClass('x-item-disabled');
10811         //}
10812     },
10813
10814     // private
10815     onShow : function(){
10816         var ae = this.getActionEl();
10817         
10818         if(ae){
10819             ae.dom.style.display = '';
10820             ae.dom.style.visibility = 'visible';
10821         }
10822     },
10823
10824     // private
10825     
10826     onHide : function(){
10827         var ae = this.getActionEl();
10828         ae.dom.style.display = 'none';
10829     },
10830
10831     /**
10832      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10833      * by an implementing function.
10834      * @method
10835      * @param {EventObject} e
10836      */
10837     onTriggerClick : Roo.emptyFn
10838 });
10839  /*
10840  * Based on:
10841  * Ext JS Library 1.1.1
10842  * Copyright(c) 2006-2007, Ext JS, LLC.
10843  *
10844  * Originally Released Under LGPL - original licence link has changed is not relivant.
10845  *
10846  * Fork - LGPL
10847  * <script type="text/javascript">
10848  */
10849
10850
10851 /**
10852  * @class Roo.data.SortTypes
10853  * @singleton
10854  * Defines the default sorting (casting?) comparison functions used when sorting data.
10855  */
10856 Roo.data.SortTypes = {
10857     /**
10858      * Default sort that does nothing
10859      * @param {Mixed} s The value being converted
10860      * @return {Mixed} The comparison value
10861      */
10862     none : function(s){
10863         return s;
10864     },
10865     
10866     /**
10867      * The regular expression used to strip tags
10868      * @type {RegExp}
10869      * @property
10870      */
10871     stripTagsRE : /<\/?[^>]+>/gi,
10872     
10873     /**
10874      * Strips all HTML tags to sort on text only
10875      * @param {Mixed} s The value being converted
10876      * @return {String} The comparison value
10877      */
10878     asText : function(s){
10879         return String(s).replace(this.stripTagsRE, "");
10880     },
10881     
10882     /**
10883      * Strips all HTML tags to sort on text only - Case insensitive
10884      * @param {Mixed} s The value being converted
10885      * @return {String} The comparison value
10886      */
10887     asUCText : function(s){
10888         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10889     },
10890     
10891     /**
10892      * Case insensitive string
10893      * @param {Mixed} s The value being converted
10894      * @return {String} The comparison value
10895      */
10896     asUCString : function(s) {
10897         return String(s).toUpperCase();
10898     },
10899     
10900     /**
10901      * Date sorting
10902      * @param {Mixed} s The value being converted
10903      * @return {Number} The comparison value
10904      */
10905     asDate : function(s) {
10906         if(!s){
10907             return 0;
10908         }
10909         if(s instanceof Date){
10910             return s.getTime();
10911         }
10912         return Date.parse(String(s));
10913     },
10914     
10915     /**
10916      * Float sorting
10917      * @param {Mixed} s The value being converted
10918      * @return {Float} The comparison value
10919      */
10920     asFloat : function(s) {
10921         var val = parseFloat(String(s).replace(/,/g, ""));
10922         if(isNaN(val)) {
10923             val = 0;
10924         }
10925         return val;
10926     },
10927     
10928     /**
10929      * Integer sorting
10930      * @param {Mixed} s The value being converted
10931      * @return {Number} The comparison value
10932      */
10933     asInt : function(s) {
10934         var val = parseInt(String(s).replace(/,/g, ""));
10935         if(isNaN(val)) {
10936             val = 0;
10937         }
10938         return val;
10939     }
10940 };/*
10941  * Based on:
10942  * Ext JS Library 1.1.1
10943  * Copyright(c) 2006-2007, Ext JS, LLC.
10944  *
10945  * Originally Released Under LGPL - original licence link has changed is not relivant.
10946  *
10947  * Fork - LGPL
10948  * <script type="text/javascript">
10949  */
10950
10951 /**
10952 * @class Roo.data.Record
10953  * Instances of this class encapsulate both record <em>definition</em> information, and record
10954  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10955  * to access Records cached in an {@link Roo.data.Store} object.<br>
10956  * <p>
10957  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10958  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10959  * objects.<br>
10960  * <p>
10961  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10962  * @constructor
10963  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10964  * {@link #create}. The parameters are the same.
10965  * @param {Array} data An associative Array of data values keyed by the field name.
10966  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10967  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10968  * not specified an integer id is generated.
10969  */
10970 Roo.data.Record = function(data, id){
10971     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10972     this.data = data;
10973 };
10974
10975 /**
10976  * Generate a constructor for a specific record layout.
10977  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10978  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10979  * Each field definition object may contain the following properties: <ul>
10980  * <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,
10981  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10982  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10983  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10984  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10985  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10986  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10987  * this may be omitted.</p></li>
10988  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10989  * <ul><li>auto (Default, implies no conversion)</li>
10990  * <li>string</li>
10991  * <li>int</li>
10992  * <li>float</li>
10993  * <li>boolean</li>
10994  * <li>date</li></ul></p></li>
10995  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10996  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10997  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10998  * by the Reader into an object that will be stored in the Record. It is passed the
10999  * following parameters:<ul>
11000  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11001  * </ul></p></li>
11002  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11003  * </ul>
11004  * <br>usage:<br><pre><code>
11005 var TopicRecord = Roo.data.Record.create(
11006     {name: 'title', mapping: 'topic_title'},
11007     {name: 'author', mapping: 'username'},
11008     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11009     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11010     {name: 'lastPoster', mapping: 'user2'},
11011     {name: 'excerpt', mapping: 'post_text'}
11012 );
11013
11014 var myNewRecord = new TopicRecord({
11015     title: 'Do my job please',
11016     author: 'noobie',
11017     totalPosts: 1,
11018     lastPost: new Date(),
11019     lastPoster: 'Animal',
11020     excerpt: 'No way dude!'
11021 });
11022 myStore.add(myNewRecord);
11023 </code></pre>
11024  * @method create
11025  * @static
11026  */
11027 Roo.data.Record.create = function(o){
11028     var f = function(){
11029         f.superclass.constructor.apply(this, arguments);
11030     };
11031     Roo.extend(f, Roo.data.Record);
11032     var p = f.prototype;
11033     p.fields = new Roo.util.MixedCollection(false, function(field){
11034         return field.name;
11035     });
11036     for(var i = 0, len = o.length; i < len; i++){
11037         p.fields.add(new Roo.data.Field(o[i]));
11038     }
11039     f.getField = function(name){
11040         return p.fields.get(name);  
11041     };
11042     return f;
11043 };
11044
11045 Roo.data.Record.AUTO_ID = 1000;
11046 Roo.data.Record.EDIT = 'edit';
11047 Roo.data.Record.REJECT = 'reject';
11048 Roo.data.Record.COMMIT = 'commit';
11049
11050 Roo.data.Record.prototype = {
11051     /**
11052      * Readonly flag - true if this record has been modified.
11053      * @type Boolean
11054      */
11055     dirty : false,
11056     editing : false,
11057     error: null,
11058     modified: null,
11059
11060     // private
11061     join : function(store){
11062         this.store = store;
11063     },
11064
11065     /**
11066      * Set the named field to the specified value.
11067      * @param {String} name The name of the field to set.
11068      * @param {Object} value The value to set the field to.
11069      */
11070     set : function(name, value){
11071         if(this.data[name] == value){
11072             return;
11073         }
11074         this.dirty = true;
11075         if(!this.modified){
11076             this.modified = {};
11077         }
11078         if(typeof this.modified[name] == 'undefined'){
11079             this.modified[name] = this.data[name];
11080         }
11081         this.data[name] = value;
11082         if(!this.editing && this.store){
11083             this.store.afterEdit(this);
11084         }       
11085     },
11086
11087     /**
11088      * Get the value of the named field.
11089      * @param {String} name The name of the field to get the value of.
11090      * @return {Object} The value of the field.
11091      */
11092     get : function(name){
11093         return this.data[name]; 
11094     },
11095
11096     // private
11097     beginEdit : function(){
11098         this.editing = true;
11099         this.modified = {}; 
11100     },
11101
11102     // private
11103     cancelEdit : function(){
11104         this.editing = false;
11105         delete this.modified;
11106     },
11107
11108     // private
11109     endEdit : function(){
11110         this.editing = false;
11111         if(this.dirty && this.store){
11112             this.store.afterEdit(this);
11113         }
11114     },
11115
11116     /**
11117      * Usually called by the {@link Roo.data.Store} which owns the Record.
11118      * Rejects all changes made to the Record since either creation, or the last commit operation.
11119      * Modified fields are reverted to their original values.
11120      * <p>
11121      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11122      * of reject operations.
11123      */
11124     reject : function(){
11125         var m = this.modified;
11126         for(var n in m){
11127             if(typeof m[n] != "function"){
11128                 this.data[n] = m[n];
11129             }
11130         }
11131         this.dirty = false;
11132         delete this.modified;
11133         this.editing = false;
11134         if(this.store){
11135             this.store.afterReject(this);
11136         }
11137     },
11138
11139     /**
11140      * Usually called by the {@link Roo.data.Store} which owns the Record.
11141      * Commits all changes made to the Record since either creation, or the last commit operation.
11142      * <p>
11143      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11144      * of commit operations.
11145      */
11146     commit : function(){
11147         this.dirty = false;
11148         delete this.modified;
11149         this.editing = false;
11150         if(this.store){
11151             this.store.afterCommit(this);
11152         }
11153     },
11154
11155     // private
11156     hasError : function(){
11157         return this.error != null;
11158     },
11159
11160     // private
11161     clearError : function(){
11162         this.error = null;
11163     },
11164
11165     /**
11166      * Creates a copy of this record.
11167      * @param {String} id (optional) A new record id if you don't want to use this record's id
11168      * @return {Record}
11169      */
11170     copy : function(newId) {
11171         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11172     }
11173 };/*
11174  * Based on:
11175  * Ext JS Library 1.1.1
11176  * Copyright(c) 2006-2007, Ext JS, LLC.
11177  *
11178  * Originally Released Under LGPL - original licence link has changed is not relivant.
11179  *
11180  * Fork - LGPL
11181  * <script type="text/javascript">
11182  */
11183
11184
11185
11186 /**
11187  * @class Roo.data.Store
11188  * @extends Roo.util.Observable
11189  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11190  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11191  * <p>
11192  * 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
11193  * has no knowledge of the format of the data returned by the Proxy.<br>
11194  * <p>
11195  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11196  * instances from the data object. These records are cached and made available through accessor functions.
11197  * @constructor
11198  * Creates a new Store.
11199  * @param {Object} config A config object containing the objects needed for the Store to access data,
11200  * and read the data into Records.
11201  */
11202 Roo.data.Store = function(config){
11203     this.data = new Roo.util.MixedCollection(false);
11204     this.data.getKey = function(o){
11205         return o.id;
11206     };
11207     this.baseParams = {};
11208     // private
11209     this.paramNames = {
11210         "start" : "start",
11211         "limit" : "limit",
11212         "sort" : "sort",
11213         "dir" : "dir",
11214         "multisort" : "_multisort"
11215     };
11216
11217     if(config && config.data){
11218         this.inlineData = config.data;
11219         delete config.data;
11220     }
11221
11222     Roo.apply(this, config);
11223     
11224     if(this.reader){ // reader passed
11225         this.reader = Roo.factory(this.reader, Roo.data);
11226         this.reader.xmodule = this.xmodule || false;
11227         if(!this.recordType){
11228             this.recordType = this.reader.recordType;
11229         }
11230         if(this.reader.onMetaChange){
11231             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11232         }
11233     }
11234
11235     if(this.recordType){
11236         this.fields = this.recordType.prototype.fields;
11237     }
11238     this.modified = [];
11239
11240     this.addEvents({
11241         /**
11242          * @event datachanged
11243          * Fires when the data cache has changed, and a widget which is using this Store
11244          * as a Record cache should refresh its view.
11245          * @param {Store} this
11246          */
11247         datachanged : true,
11248         /**
11249          * @event metachange
11250          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11251          * @param {Store} this
11252          * @param {Object} meta The JSON metadata
11253          */
11254         metachange : true,
11255         /**
11256          * @event add
11257          * Fires when Records have been added to the Store
11258          * @param {Store} this
11259          * @param {Roo.data.Record[]} records The array of Records added
11260          * @param {Number} index The index at which the record(s) were added
11261          */
11262         add : true,
11263         /**
11264          * @event remove
11265          * Fires when a Record has been removed from the Store
11266          * @param {Store} this
11267          * @param {Roo.data.Record} record The Record that was removed
11268          * @param {Number} index The index at which the record was removed
11269          */
11270         remove : true,
11271         /**
11272          * @event update
11273          * Fires when a Record has been updated
11274          * @param {Store} this
11275          * @param {Roo.data.Record} record The Record that was updated
11276          * @param {String} operation The update operation being performed.  Value may be one of:
11277          * <pre><code>
11278  Roo.data.Record.EDIT
11279  Roo.data.Record.REJECT
11280  Roo.data.Record.COMMIT
11281          * </code></pre>
11282          */
11283         update : true,
11284         /**
11285          * @event clear
11286          * Fires when the data cache has been cleared.
11287          * @param {Store} this
11288          */
11289         clear : true,
11290         /**
11291          * @event beforeload
11292          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11293          * the load action will be canceled.
11294          * @param {Store} this
11295          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11296          */
11297         beforeload : true,
11298         /**
11299          * @event beforeloadadd
11300          * Fires after a new set of Records has been loaded.
11301          * @param {Store} this
11302          * @param {Roo.data.Record[]} records The Records that were loaded
11303          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11304          */
11305         beforeloadadd : true,
11306         /**
11307          * @event load
11308          * Fires after a new set of Records has been loaded, before they are added to the store.
11309          * @param {Store} this
11310          * @param {Roo.data.Record[]} records The Records that were loaded
11311          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11312          * @params {Object} return from reader
11313          */
11314         load : true,
11315         /**
11316          * @event loadexception
11317          * Fires if an exception occurs in the Proxy during loading.
11318          * Called with the signature of the Proxy's "loadexception" event.
11319          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11320          * 
11321          * @param {Proxy} 
11322          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11323          * @param {Object} load options 
11324          * @param {Object} jsonData from your request (normally this contains the Exception)
11325          */
11326         loadexception : true
11327     });
11328     
11329     if(this.proxy){
11330         this.proxy = Roo.factory(this.proxy, Roo.data);
11331         this.proxy.xmodule = this.xmodule || false;
11332         this.relayEvents(this.proxy,  ["loadexception"]);
11333     }
11334     this.sortToggle = {};
11335     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11336
11337     Roo.data.Store.superclass.constructor.call(this);
11338
11339     if(this.inlineData){
11340         this.loadData(this.inlineData);
11341         delete this.inlineData;
11342     }
11343 };
11344
11345 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11346      /**
11347     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11348     * without a remote query - used by combo/forms at present.
11349     */
11350     
11351     /**
11352     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11353     */
11354     /**
11355     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11356     */
11357     /**
11358     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11359     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11360     */
11361     /**
11362     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11363     * on any HTTP request
11364     */
11365     /**
11366     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11367     */
11368     /**
11369     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11370     */
11371     multiSort: false,
11372     /**
11373     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11374     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11375     */
11376     remoteSort : false,
11377
11378     /**
11379     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11380      * loaded or when a record is removed. (defaults to false).
11381     */
11382     pruneModifiedRecords : false,
11383
11384     // private
11385     lastOptions : null,
11386
11387     /**
11388      * Add Records to the Store and fires the add event.
11389      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11390      */
11391     add : function(records){
11392         records = [].concat(records);
11393         for(var i = 0, len = records.length; i < len; i++){
11394             records[i].join(this);
11395         }
11396         var index = this.data.length;
11397         this.data.addAll(records);
11398         this.fireEvent("add", this, records, index);
11399     },
11400
11401     /**
11402      * Remove a Record from the Store and fires the remove event.
11403      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11404      */
11405     remove : function(record){
11406         var index = this.data.indexOf(record);
11407         this.data.removeAt(index);
11408  
11409         if(this.pruneModifiedRecords){
11410             this.modified.remove(record);
11411         }
11412         this.fireEvent("remove", this, record, index);
11413     },
11414
11415     /**
11416      * Remove all Records from the Store and fires the clear event.
11417      */
11418     removeAll : function(){
11419         this.data.clear();
11420         if(this.pruneModifiedRecords){
11421             this.modified = [];
11422         }
11423         this.fireEvent("clear", this);
11424     },
11425
11426     /**
11427      * Inserts Records to the Store at the given index and fires the add event.
11428      * @param {Number} index The start index at which to insert the passed Records.
11429      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11430      */
11431     insert : function(index, records){
11432         records = [].concat(records);
11433         for(var i = 0, len = records.length; i < len; i++){
11434             this.data.insert(index, records[i]);
11435             records[i].join(this);
11436         }
11437         this.fireEvent("add", this, records, index);
11438     },
11439
11440     /**
11441      * Get the index within the cache of the passed Record.
11442      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11443      * @return {Number} The index of the passed Record. Returns -1 if not found.
11444      */
11445     indexOf : function(record){
11446         return this.data.indexOf(record);
11447     },
11448
11449     /**
11450      * Get the index within the cache of the Record with the passed id.
11451      * @param {String} id The id of the Record to find.
11452      * @return {Number} The index of the Record. Returns -1 if not found.
11453      */
11454     indexOfId : function(id){
11455         return this.data.indexOfKey(id);
11456     },
11457
11458     /**
11459      * Get the Record with the specified id.
11460      * @param {String} id The id of the Record to find.
11461      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11462      */
11463     getById : function(id){
11464         return this.data.key(id);
11465     },
11466
11467     /**
11468      * Get the Record at the specified index.
11469      * @param {Number} index The index of the Record to find.
11470      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11471      */
11472     getAt : function(index){
11473         return this.data.itemAt(index);
11474     },
11475
11476     /**
11477      * Returns a range of Records between specified indices.
11478      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11479      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11480      * @return {Roo.data.Record[]} An array of Records
11481      */
11482     getRange : function(start, end){
11483         return this.data.getRange(start, end);
11484     },
11485
11486     // private
11487     storeOptions : function(o){
11488         o = Roo.apply({}, o);
11489         delete o.callback;
11490         delete o.scope;
11491         this.lastOptions = o;
11492     },
11493
11494     /**
11495      * Loads the Record cache from the configured Proxy using the configured Reader.
11496      * <p>
11497      * If using remote paging, then the first load call must specify the <em>start</em>
11498      * and <em>limit</em> properties in the options.params property to establish the initial
11499      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11500      * <p>
11501      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11502      * and this call will return before the new data has been loaded. Perform any post-processing
11503      * in a callback function, or in a "load" event handler.</strong>
11504      * <p>
11505      * @param {Object} options An object containing properties which control loading options:<ul>
11506      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11507      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11508      * passed the following arguments:<ul>
11509      * <li>r : Roo.data.Record[]</li>
11510      * <li>options: Options object from the load call</li>
11511      * <li>success: Boolean success indicator</li></ul></li>
11512      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11513      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11514      * </ul>
11515      */
11516     load : function(options){
11517         options = options || {};
11518         if(this.fireEvent("beforeload", this, options) !== false){
11519             this.storeOptions(options);
11520             var p = Roo.apply(options.params || {}, this.baseParams);
11521             // if meta was not loaded from remote source.. try requesting it.
11522             if (!this.reader.metaFromRemote) {
11523                 p._requestMeta = 1;
11524             }
11525             if(this.sortInfo && this.remoteSort){
11526                 var pn = this.paramNames;
11527                 p[pn["sort"]] = this.sortInfo.field;
11528                 p[pn["dir"]] = this.sortInfo.direction;
11529             }
11530             if (this.multiSort) {
11531                 var pn = this.paramNames;
11532                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11533             }
11534             
11535             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11536         }
11537     },
11538
11539     /**
11540      * Reloads the Record cache from the configured Proxy using the configured Reader and
11541      * the options from the last load operation performed.
11542      * @param {Object} options (optional) An object containing properties which may override the options
11543      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11544      * the most recently used options are reused).
11545      */
11546     reload : function(options){
11547         this.load(Roo.applyIf(options||{}, this.lastOptions));
11548     },
11549
11550     // private
11551     // Called as a callback by the Reader during a load operation.
11552     loadRecords : function(o, options, success){
11553         if(!o || success === false){
11554             if(success !== false){
11555                 this.fireEvent("load", this, [], options, o);
11556             }
11557             if(options.callback){
11558                 options.callback.call(options.scope || this, [], options, false);
11559             }
11560             return;
11561         }
11562         // if data returned failure - throw an exception.
11563         if (o.success === false) {
11564             // show a message if no listener is registered.
11565             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11566                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11567             }
11568             // loadmask wil be hooked into this..
11569             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11570             return;
11571         }
11572         var r = o.records, t = o.totalRecords || r.length;
11573         
11574         this.fireEvent("beforeloadadd", this, r, options, o);
11575         
11576         if(!options || options.add !== true){
11577             if(this.pruneModifiedRecords){
11578                 this.modified = [];
11579             }
11580             for(var i = 0, len = r.length; i < len; i++){
11581                 r[i].join(this);
11582             }
11583             if(this.snapshot){
11584                 this.data = this.snapshot;
11585                 delete this.snapshot;
11586             }
11587             this.data.clear();
11588             this.data.addAll(r);
11589             this.totalLength = t;
11590             this.applySort();
11591             this.fireEvent("datachanged", this);
11592         }else{
11593             this.totalLength = Math.max(t, this.data.length+r.length);
11594             this.add(r);
11595         }
11596         
11597         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11598                 
11599             var e = new Roo.data.Record({});
11600
11601             e.set(this.parent.displayField, this.parent.emptyTitle);
11602             e.set(this.parent.valueField, '');
11603
11604             this.insert(0, e);
11605         }
11606             
11607         this.fireEvent("load", this, r, options, o);
11608         if(options.callback){
11609             options.callback.call(options.scope || this, r, options, true);
11610         }
11611     },
11612
11613
11614     /**
11615      * Loads data from a passed data block. A Reader which understands the format of the data
11616      * must have been configured in the constructor.
11617      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11618      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11619      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11620      */
11621     loadData : function(o, append){
11622         var r = this.reader.readRecords(o);
11623         this.loadRecords(r, {add: append}, true);
11624     },
11625
11626     /**
11627      * Gets the number of cached records.
11628      * <p>
11629      * <em>If using paging, this may not be the total size of the dataset. If the data object
11630      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11631      * the data set size</em>
11632      */
11633     getCount : function(){
11634         return this.data.length || 0;
11635     },
11636
11637     /**
11638      * Gets the total number of records in the dataset as returned by the server.
11639      * <p>
11640      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11641      * the dataset size</em>
11642      */
11643     getTotalCount : function(){
11644         return this.totalLength || 0;
11645     },
11646
11647     /**
11648      * Returns the sort state of the Store as an object with two properties:
11649      * <pre><code>
11650  field {String} The name of the field by which the Records are sorted
11651  direction {String} The sort order, "ASC" or "DESC"
11652      * </code></pre>
11653      */
11654     getSortState : function(){
11655         return this.sortInfo;
11656     },
11657
11658     // private
11659     applySort : function(){
11660         if(this.sortInfo && !this.remoteSort){
11661             var s = this.sortInfo, f = s.field;
11662             var st = this.fields.get(f).sortType;
11663             var fn = function(r1, r2){
11664                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11665                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11666             };
11667             this.data.sort(s.direction, fn);
11668             if(this.snapshot && this.snapshot != this.data){
11669                 this.snapshot.sort(s.direction, fn);
11670             }
11671         }
11672     },
11673
11674     /**
11675      * Sets the default sort column and order to be used by the next load operation.
11676      * @param {String} fieldName The name of the field to sort by.
11677      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11678      */
11679     setDefaultSort : function(field, dir){
11680         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11681     },
11682
11683     /**
11684      * Sort the Records.
11685      * If remote sorting is used, the sort is performed on the server, and the cache is
11686      * reloaded. If local sorting is used, the cache is sorted internally.
11687      * @param {String} fieldName The name of the field to sort by.
11688      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11689      */
11690     sort : function(fieldName, dir){
11691         var f = this.fields.get(fieldName);
11692         if(!dir){
11693             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11694             
11695             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11696                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11697             }else{
11698                 dir = f.sortDir;
11699             }
11700         }
11701         this.sortToggle[f.name] = dir;
11702         this.sortInfo = {field: f.name, direction: dir};
11703         if(!this.remoteSort){
11704             this.applySort();
11705             this.fireEvent("datachanged", this);
11706         }else{
11707             this.load(this.lastOptions);
11708         }
11709     },
11710
11711     /**
11712      * Calls the specified function for each of the Records in the cache.
11713      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11714      * Returning <em>false</em> aborts and exits the iteration.
11715      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11716      */
11717     each : function(fn, scope){
11718         this.data.each(fn, scope);
11719     },
11720
11721     /**
11722      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11723      * (e.g., during paging).
11724      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11725      */
11726     getModifiedRecords : function(){
11727         return this.modified;
11728     },
11729
11730     // private
11731     createFilterFn : function(property, value, anyMatch){
11732         if(!value.exec){ // not a regex
11733             value = String(value);
11734             if(value.length == 0){
11735                 return false;
11736             }
11737             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11738         }
11739         return function(r){
11740             return value.test(r.data[property]);
11741         };
11742     },
11743
11744     /**
11745      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11746      * @param {String} property A field on your records
11747      * @param {Number} start The record index to start at (defaults to 0)
11748      * @param {Number} end The last record index to include (defaults to length - 1)
11749      * @return {Number} The sum
11750      */
11751     sum : function(property, start, end){
11752         var rs = this.data.items, v = 0;
11753         start = start || 0;
11754         end = (end || end === 0) ? end : rs.length-1;
11755
11756         for(var i = start; i <= end; i++){
11757             v += (rs[i].data[property] || 0);
11758         }
11759         return v;
11760     },
11761
11762     /**
11763      * Filter the records by a specified property.
11764      * @param {String} field A field on your records
11765      * @param {String/RegExp} value Either a string that the field
11766      * should start with or a RegExp to test against the field
11767      * @param {Boolean} anyMatch True to match any part not just the beginning
11768      */
11769     filter : function(property, value, anyMatch){
11770         var fn = this.createFilterFn(property, value, anyMatch);
11771         return fn ? this.filterBy(fn) : this.clearFilter();
11772     },
11773
11774     /**
11775      * Filter by a function. The specified function will be called with each
11776      * record in this data source. If the function returns true the record is included,
11777      * otherwise it is filtered.
11778      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11779      * @param {Object} scope (optional) The scope of the function (defaults to this)
11780      */
11781     filterBy : function(fn, scope){
11782         this.snapshot = this.snapshot || this.data;
11783         this.data = this.queryBy(fn, scope||this);
11784         this.fireEvent("datachanged", this);
11785     },
11786
11787     /**
11788      * Query the records by a specified property.
11789      * @param {String} field A field on your records
11790      * @param {String/RegExp} value Either a string that the field
11791      * should start with or a RegExp to test against the field
11792      * @param {Boolean} anyMatch True to match any part not just the beginning
11793      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11794      */
11795     query : function(property, value, anyMatch){
11796         var fn = this.createFilterFn(property, value, anyMatch);
11797         return fn ? this.queryBy(fn) : this.data.clone();
11798     },
11799
11800     /**
11801      * Query by a function. The specified function will be called with each
11802      * record in this data source. If the function returns true the record is included
11803      * in the results.
11804      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11805      * @param {Object} scope (optional) The scope of the function (defaults to this)
11806       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11807      **/
11808     queryBy : function(fn, scope){
11809         var data = this.snapshot || this.data;
11810         return data.filterBy(fn, scope||this);
11811     },
11812
11813     /**
11814      * Collects unique values for a particular dataIndex from this store.
11815      * @param {String} dataIndex The property to collect
11816      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11817      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11818      * @return {Array} An array of the unique values
11819      **/
11820     collect : function(dataIndex, allowNull, bypassFilter){
11821         var d = (bypassFilter === true && this.snapshot) ?
11822                 this.snapshot.items : this.data.items;
11823         var v, sv, r = [], l = {};
11824         for(var i = 0, len = d.length; i < len; i++){
11825             v = d[i].data[dataIndex];
11826             sv = String(v);
11827             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11828                 l[sv] = true;
11829                 r[r.length] = v;
11830             }
11831         }
11832         return r;
11833     },
11834
11835     /**
11836      * Revert to a view of the Record cache with no filtering applied.
11837      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11838      */
11839     clearFilter : function(suppressEvent){
11840         if(this.snapshot && this.snapshot != this.data){
11841             this.data = this.snapshot;
11842             delete this.snapshot;
11843             if(suppressEvent !== true){
11844                 this.fireEvent("datachanged", this);
11845             }
11846         }
11847     },
11848
11849     // private
11850     afterEdit : function(record){
11851         if(this.modified.indexOf(record) == -1){
11852             this.modified.push(record);
11853         }
11854         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11855     },
11856     
11857     // private
11858     afterReject : function(record){
11859         this.modified.remove(record);
11860         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11861     },
11862
11863     // private
11864     afterCommit : function(record){
11865         this.modified.remove(record);
11866         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11867     },
11868
11869     /**
11870      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11871      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11872      */
11873     commitChanges : function(){
11874         var m = this.modified.slice(0);
11875         this.modified = [];
11876         for(var i = 0, len = m.length; i < len; i++){
11877             m[i].commit();
11878         }
11879     },
11880
11881     /**
11882      * Cancel outstanding changes on all changed records.
11883      */
11884     rejectChanges : function(){
11885         var m = this.modified.slice(0);
11886         this.modified = [];
11887         for(var i = 0, len = m.length; i < len; i++){
11888             m[i].reject();
11889         }
11890     },
11891
11892     onMetaChange : function(meta, rtype, o){
11893         this.recordType = rtype;
11894         this.fields = rtype.prototype.fields;
11895         delete this.snapshot;
11896         this.sortInfo = meta.sortInfo || this.sortInfo;
11897         this.modified = [];
11898         this.fireEvent('metachange', this, this.reader.meta);
11899     },
11900     
11901     moveIndex : function(data, type)
11902     {
11903         var index = this.indexOf(data);
11904         
11905         var newIndex = index + type;
11906         
11907         this.remove(data);
11908         
11909         this.insert(newIndex, data);
11910         
11911     }
11912 });/*
11913  * Based on:
11914  * Ext JS Library 1.1.1
11915  * Copyright(c) 2006-2007, Ext JS, LLC.
11916  *
11917  * Originally Released Under LGPL - original licence link has changed is not relivant.
11918  *
11919  * Fork - LGPL
11920  * <script type="text/javascript">
11921  */
11922
11923 /**
11924  * @class Roo.data.SimpleStore
11925  * @extends Roo.data.Store
11926  * Small helper class to make creating Stores from Array data easier.
11927  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11928  * @cfg {Array} fields An array of field definition objects, or field name strings.
11929  * @cfg {Array} data The multi-dimensional array of data
11930  * @constructor
11931  * @param {Object} config
11932  */
11933 Roo.data.SimpleStore = function(config){
11934     Roo.data.SimpleStore.superclass.constructor.call(this, {
11935         isLocal : true,
11936         reader: new Roo.data.ArrayReader({
11937                 id: config.id
11938             },
11939             Roo.data.Record.create(config.fields)
11940         ),
11941         proxy : new Roo.data.MemoryProxy(config.data)
11942     });
11943     this.load();
11944 };
11945 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11946  * Based on:
11947  * Ext JS Library 1.1.1
11948  * Copyright(c) 2006-2007, Ext JS, LLC.
11949  *
11950  * Originally Released Under LGPL - original licence link has changed is not relivant.
11951  *
11952  * Fork - LGPL
11953  * <script type="text/javascript">
11954  */
11955
11956 /**
11957 /**
11958  * @extends Roo.data.Store
11959  * @class Roo.data.JsonStore
11960  * Small helper class to make creating Stores for JSON data easier. <br/>
11961 <pre><code>
11962 var store = new Roo.data.JsonStore({
11963     url: 'get-images.php',
11964     root: 'images',
11965     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11966 });
11967 </code></pre>
11968  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11969  * JsonReader and HttpProxy (unless inline data is provided).</b>
11970  * @cfg {Array} fields An array of field definition objects, or field name strings.
11971  * @constructor
11972  * @param {Object} config
11973  */
11974 Roo.data.JsonStore = function(c){
11975     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11976         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11977         reader: new Roo.data.JsonReader(c, c.fields)
11978     }));
11979 };
11980 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990
11991  
11992 Roo.data.Field = function(config){
11993     if(typeof config == "string"){
11994         config = {name: config};
11995     }
11996     Roo.apply(this, config);
11997     
11998     if(!this.type){
11999         this.type = "auto";
12000     }
12001     
12002     var st = Roo.data.SortTypes;
12003     // named sortTypes are supported, here we look them up
12004     if(typeof this.sortType == "string"){
12005         this.sortType = st[this.sortType];
12006     }
12007     
12008     // set default sortType for strings and dates
12009     if(!this.sortType){
12010         switch(this.type){
12011             case "string":
12012                 this.sortType = st.asUCString;
12013                 break;
12014             case "date":
12015                 this.sortType = st.asDate;
12016                 break;
12017             default:
12018                 this.sortType = st.none;
12019         }
12020     }
12021
12022     // define once
12023     var stripRe = /[\$,%]/g;
12024
12025     // prebuilt conversion function for this field, instead of
12026     // switching every time we're reading a value
12027     if(!this.convert){
12028         var cv, dateFormat = this.dateFormat;
12029         switch(this.type){
12030             case "":
12031             case "auto":
12032             case undefined:
12033                 cv = function(v){ return v; };
12034                 break;
12035             case "string":
12036                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12037                 break;
12038             case "int":
12039                 cv = function(v){
12040                     return v !== undefined && v !== null && v !== '' ?
12041                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12042                     };
12043                 break;
12044             case "float":
12045                 cv = function(v){
12046                     return v !== undefined && v !== null && v !== '' ?
12047                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12048                     };
12049                 break;
12050             case "bool":
12051             case "boolean":
12052                 cv = function(v){ return v === true || v === "true" || v == 1; };
12053                 break;
12054             case "date":
12055                 cv = function(v){
12056                     if(!v){
12057                         return '';
12058                     }
12059                     if(v instanceof Date){
12060                         return v;
12061                     }
12062                     if(dateFormat){
12063                         if(dateFormat == "timestamp"){
12064                             return new Date(v*1000);
12065                         }
12066                         return Date.parseDate(v, dateFormat);
12067                     }
12068                     var parsed = Date.parse(v);
12069                     return parsed ? new Date(parsed) : null;
12070                 };
12071              break;
12072             
12073         }
12074         this.convert = cv;
12075     }
12076 };
12077
12078 Roo.data.Field.prototype = {
12079     dateFormat: null,
12080     defaultValue: "",
12081     mapping: null,
12082     sortType : null,
12083     sortDir : "ASC"
12084 };/*
12085  * Based on:
12086  * Ext JS Library 1.1.1
12087  * Copyright(c) 2006-2007, Ext JS, LLC.
12088  *
12089  * Originally Released Under LGPL - original licence link has changed is not relivant.
12090  *
12091  * Fork - LGPL
12092  * <script type="text/javascript">
12093  */
12094  
12095 // Base class for reading structured data from a data source.  This class is intended to be
12096 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12097
12098 /**
12099  * @class Roo.data.DataReader
12100  * Base class for reading structured data from a data source.  This class is intended to be
12101  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12102  */
12103
12104 Roo.data.DataReader = function(meta, recordType){
12105     
12106     this.meta = meta;
12107     
12108     this.recordType = recordType instanceof Array ? 
12109         Roo.data.Record.create(recordType) : recordType;
12110 };
12111
12112 Roo.data.DataReader.prototype = {
12113      /**
12114      * Create an empty record
12115      * @param {Object} data (optional) - overlay some values
12116      * @return {Roo.data.Record} record created.
12117      */
12118     newRow :  function(d) {
12119         var da =  {};
12120         this.recordType.prototype.fields.each(function(c) {
12121             switch( c.type) {
12122                 case 'int' : da[c.name] = 0; break;
12123                 case 'date' : da[c.name] = new Date(); break;
12124                 case 'float' : da[c.name] = 0.0; break;
12125                 case 'boolean' : da[c.name] = false; break;
12126                 default : da[c.name] = ""; break;
12127             }
12128             
12129         });
12130         return new this.recordType(Roo.apply(da, d));
12131     }
12132     
12133 };/*
12134  * Based on:
12135  * Ext JS Library 1.1.1
12136  * Copyright(c) 2006-2007, Ext JS, LLC.
12137  *
12138  * Originally Released Under LGPL - original licence link has changed is not relivant.
12139  *
12140  * Fork - LGPL
12141  * <script type="text/javascript">
12142  */
12143
12144 /**
12145  * @class Roo.data.DataProxy
12146  * @extends Roo.data.Observable
12147  * This class is an abstract base class for implementations which provide retrieval of
12148  * unformatted data objects.<br>
12149  * <p>
12150  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12151  * (of the appropriate type which knows how to parse the data object) to provide a block of
12152  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12153  * <p>
12154  * Custom implementations must implement the load method as described in
12155  * {@link Roo.data.HttpProxy#load}.
12156  */
12157 Roo.data.DataProxy = function(){
12158     this.addEvents({
12159         /**
12160          * @event beforeload
12161          * Fires before a network request is made to retrieve a data object.
12162          * @param {Object} This DataProxy object.
12163          * @param {Object} params The params parameter to the load function.
12164          */
12165         beforeload : true,
12166         /**
12167          * @event load
12168          * Fires before the load method's callback is called.
12169          * @param {Object} This DataProxy object.
12170          * @param {Object} o The data object.
12171          * @param {Object} arg The callback argument object passed to the load function.
12172          */
12173         load : true,
12174         /**
12175          * @event loadexception
12176          * Fires if an Exception occurs during data retrieval.
12177          * @param {Object} This DataProxy object.
12178          * @param {Object} o The data object.
12179          * @param {Object} arg The callback argument object passed to the load function.
12180          * @param {Object} e The Exception.
12181          */
12182         loadexception : true
12183     });
12184     Roo.data.DataProxy.superclass.constructor.call(this);
12185 };
12186
12187 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12188
12189     /**
12190      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12191      */
12192 /*
12193  * Based on:
12194  * Ext JS Library 1.1.1
12195  * Copyright(c) 2006-2007, Ext JS, LLC.
12196  *
12197  * Originally Released Under LGPL - original licence link has changed is not relivant.
12198  *
12199  * Fork - LGPL
12200  * <script type="text/javascript">
12201  */
12202 /**
12203  * @class Roo.data.MemoryProxy
12204  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12205  * to the Reader when its load method is called.
12206  * @constructor
12207  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12208  */
12209 Roo.data.MemoryProxy = function(data){
12210     if (data.data) {
12211         data = data.data;
12212     }
12213     Roo.data.MemoryProxy.superclass.constructor.call(this);
12214     this.data = data;
12215 };
12216
12217 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12218     
12219     /**
12220      * Load data from the requested source (in this case an in-memory
12221      * data object passed to the constructor), read the data object into
12222      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12223      * process that block using the passed callback.
12224      * @param {Object} params This parameter is not used by the MemoryProxy class.
12225      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12226      * object into a block of Roo.data.Records.
12227      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12228      * The function must be passed <ul>
12229      * <li>The Record block object</li>
12230      * <li>The "arg" argument from the load function</li>
12231      * <li>A boolean success indicator</li>
12232      * </ul>
12233      * @param {Object} scope The scope in which to call the callback
12234      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12235      */
12236     load : function(params, reader, callback, scope, arg){
12237         params = params || {};
12238         var result;
12239         try {
12240             result = reader.readRecords(this.data);
12241         }catch(e){
12242             this.fireEvent("loadexception", this, arg, null, e);
12243             callback.call(scope, null, arg, false);
12244             return;
12245         }
12246         callback.call(scope, result, arg, true);
12247     },
12248     
12249     // private
12250     update : function(params, records){
12251         
12252     }
12253 });/*
12254  * Based on:
12255  * Ext JS Library 1.1.1
12256  * Copyright(c) 2006-2007, Ext JS, LLC.
12257  *
12258  * Originally Released Under LGPL - original licence link has changed is not relivant.
12259  *
12260  * Fork - LGPL
12261  * <script type="text/javascript">
12262  */
12263 /**
12264  * @class Roo.data.HttpProxy
12265  * @extends Roo.data.DataProxy
12266  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12267  * configured to reference a certain URL.<br><br>
12268  * <p>
12269  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12270  * from which the running page was served.<br><br>
12271  * <p>
12272  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12273  * <p>
12274  * Be aware that to enable the browser to parse an XML document, the server must set
12275  * the Content-Type header in the HTTP response to "text/xml".
12276  * @constructor
12277  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12278  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12279  * will be used to make the request.
12280  */
12281 Roo.data.HttpProxy = function(conn){
12282     Roo.data.HttpProxy.superclass.constructor.call(this);
12283     // is conn a conn config or a real conn?
12284     this.conn = conn;
12285     this.useAjax = !conn || !conn.events;
12286   
12287 };
12288
12289 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12290     // thse are take from connection...
12291     
12292     /**
12293      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12294      */
12295     /**
12296      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12297      * extra parameters to each request made by this object. (defaults to undefined)
12298      */
12299     /**
12300      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12301      *  to each request made by this object. (defaults to undefined)
12302      */
12303     /**
12304      * @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)
12305      */
12306     /**
12307      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12308      */
12309      /**
12310      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12311      * @type Boolean
12312      */
12313   
12314
12315     /**
12316      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12317      * @type Boolean
12318      */
12319     /**
12320      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12321      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12322      * a finer-grained basis than the DataProxy events.
12323      */
12324     getConnection : function(){
12325         return this.useAjax ? Roo.Ajax : this.conn;
12326     },
12327
12328     /**
12329      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12330      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12331      * process that block using the passed callback.
12332      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12333      * for the request to the remote server.
12334      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12335      * object into a block of Roo.data.Records.
12336      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12337      * The function must be passed <ul>
12338      * <li>The Record block object</li>
12339      * <li>The "arg" argument from the load function</li>
12340      * <li>A boolean success indicator</li>
12341      * </ul>
12342      * @param {Object} scope The scope in which to call the callback
12343      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12344      */
12345     load : function(params, reader, callback, scope, arg){
12346         if(this.fireEvent("beforeload", this, params) !== false){
12347             var  o = {
12348                 params : params || {},
12349                 request: {
12350                     callback : callback,
12351                     scope : scope,
12352                     arg : arg
12353                 },
12354                 reader: reader,
12355                 callback : this.loadResponse,
12356                 scope: this
12357             };
12358             if(this.useAjax){
12359                 Roo.applyIf(o, this.conn);
12360                 if(this.activeRequest){
12361                     Roo.Ajax.abort(this.activeRequest);
12362                 }
12363                 this.activeRequest = Roo.Ajax.request(o);
12364             }else{
12365                 this.conn.request(o);
12366             }
12367         }else{
12368             callback.call(scope||this, null, arg, false);
12369         }
12370     },
12371
12372     // private
12373     loadResponse : function(o, success, response){
12374         delete this.activeRequest;
12375         if(!success){
12376             this.fireEvent("loadexception", this, o, response);
12377             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12378             return;
12379         }
12380         var result;
12381         try {
12382             result = o.reader.read(response);
12383         }catch(e){
12384             this.fireEvent("loadexception", this, o, response, e);
12385             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12386             return;
12387         }
12388         
12389         this.fireEvent("load", this, o, o.request.arg);
12390         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12391     },
12392
12393     // private
12394     update : function(dataSet){
12395
12396     },
12397
12398     // private
12399     updateResponse : function(dataSet){
12400
12401     }
12402 });/*
12403  * Based on:
12404  * Ext JS Library 1.1.1
12405  * Copyright(c) 2006-2007, Ext JS, LLC.
12406  *
12407  * Originally Released Under LGPL - original licence link has changed is not relivant.
12408  *
12409  * Fork - LGPL
12410  * <script type="text/javascript">
12411  */
12412
12413 /**
12414  * @class Roo.data.ScriptTagProxy
12415  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12416  * other than the originating domain of the running page.<br><br>
12417  * <p>
12418  * <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
12419  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12420  * <p>
12421  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12422  * source code that is used as the source inside a &lt;script> tag.<br><br>
12423  * <p>
12424  * In order for the browser to process the returned data, the server must wrap the data object
12425  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12426  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12427  * depending on whether the callback name was passed:
12428  * <p>
12429  * <pre><code>
12430 boolean scriptTag = false;
12431 String cb = request.getParameter("callback");
12432 if (cb != null) {
12433     scriptTag = true;
12434     response.setContentType("text/javascript");
12435 } else {
12436     response.setContentType("application/x-json");
12437 }
12438 Writer out = response.getWriter();
12439 if (scriptTag) {
12440     out.write(cb + "(");
12441 }
12442 out.print(dataBlock.toJsonString());
12443 if (scriptTag) {
12444     out.write(");");
12445 }
12446 </pre></code>
12447  *
12448  * @constructor
12449  * @param {Object} config A configuration object.
12450  */
12451 Roo.data.ScriptTagProxy = function(config){
12452     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12453     Roo.apply(this, config);
12454     this.head = document.getElementsByTagName("head")[0];
12455 };
12456
12457 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12458
12459 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12460     /**
12461      * @cfg {String} url The URL from which to request the data object.
12462      */
12463     /**
12464      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12465      */
12466     timeout : 30000,
12467     /**
12468      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12469      * the server the name of the callback function set up by the load call to process the returned data object.
12470      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12471      * javascript output which calls this named function passing the data object as its only parameter.
12472      */
12473     callbackParam : "callback",
12474     /**
12475      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12476      * name to the request.
12477      */
12478     nocache : true,
12479
12480     /**
12481      * Load data from the configured URL, read the data object into
12482      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12483      * process that block using the passed callback.
12484      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12485      * for the request to the remote server.
12486      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12487      * object into a block of Roo.data.Records.
12488      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12489      * The function must be passed <ul>
12490      * <li>The Record block object</li>
12491      * <li>The "arg" argument from the load function</li>
12492      * <li>A boolean success indicator</li>
12493      * </ul>
12494      * @param {Object} scope The scope in which to call the callback
12495      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12496      */
12497     load : function(params, reader, callback, scope, arg){
12498         if(this.fireEvent("beforeload", this, params) !== false){
12499
12500             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12501
12502             var url = this.url;
12503             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12504             if(this.nocache){
12505                 url += "&_dc=" + (new Date().getTime());
12506             }
12507             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12508             var trans = {
12509                 id : transId,
12510                 cb : "stcCallback"+transId,
12511                 scriptId : "stcScript"+transId,
12512                 params : params,
12513                 arg : arg,
12514                 url : url,
12515                 callback : callback,
12516                 scope : scope,
12517                 reader : reader
12518             };
12519             var conn = this;
12520
12521             window[trans.cb] = function(o){
12522                 conn.handleResponse(o, trans);
12523             };
12524
12525             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12526
12527             if(this.autoAbort !== false){
12528                 this.abort();
12529             }
12530
12531             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12532
12533             var script = document.createElement("script");
12534             script.setAttribute("src", url);
12535             script.setAttribute("type", "text/javascript");
12536             script.setAttribute("id", trans.scriptId);
12537             this.head.appendChild(script);
12538
12539             this.trans = trans;
12540         }else{
12541             callback.call(scope||this, null, arg, false);
12542         }
12543     },
12544
12545     // private
12546     isLoading : function(){
12547         return this.trans ? true : false;
12548     },
12549
12550     /**
12551      * Abort the current server request.
12552      */
12553     abort : function(){
12554         if(this.isLoading()){
12555             this.destroyTrans(this.trans);
12556         }
12557     },
12558
12559     // private
12560     destroyTrans : function(trans, isLoaded){
12561         this.head.removeChild(document.getElementById(trans.scriptId));
12562         clearTimeout(trans.timeoutId);
12563         if(isLoaded){
12564             window[trans.cb] = undefined;
12565             try{
12566                 delete window[trans.cb];
12567             }catch(e){}
12568         }else{
12569             // if hasn't been loaded, wait for load to remove it to prevent script error
12570             window[trans.cb] = function(){
12571                 window[trans.cb] = undefined;
12572                 try{
12573                     delete window[trans.cb];
12574                 }catch(e){}
12575             };
12576         }
12577     },
12578
12579     // private
12580     handleResponse : function(o, trans){
12581         this.trans = false;
12582         this.destroyTrans(trans, true);
12583         var result;
12584         try {
12585             result = trans.reader.readRecords(o);
12586         }catch(e){
12587             this.fireEvent("loadexception", this, o, trans.arg, e);
12588             trans.callback.call(trans.scope||window, null, trans.arg, false);
12589             return;
12590         }
12591         this.fireEvent("load", this, o, trans.arg);
12592         trans.callback.call(trans.scope||window, result, trans.arg, true);
12593     },
12594
12595     // private
12596     handleFailure : function(trans){
12597         this.trans = false;
12598         this.destroyTrans(trans, false);
12599         this.fireEvent("loadexception", this, null, trans.arg);
12600         trans.callback.call(trans.scope||window, null, trans.arg, false);
12601     }
12602 });/*
12603  * Based on:
12604  * Ext JS Library 1.1.1
12605  * Copyright(c) 2006-2007, Ext JS, LLC.
12606  *
12607  * Originally Released Under LGPL - original licence link has changed is not relivant.
12608  *
12609  * Fork - LGPL
12610  * <script type="text/javascript">
12611  */
12612
12613 /**
12614  * @class Roo.data.JsonReader
12615  * @extends Roo.data.DataReader
12616  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12617  * based on mappings in a provided Roo.data.Record constructor.
12618  * 
12619  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12620  * in the reply previously. 
12621  * 
12622  * <p>
12623  * Example code:
12624  * <pre><code>
12625 var RecordDef = Roo.data.Record.create([
12626     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12627     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12628 ]);
12629 var myReader = new Roo.data.JsonReader({
12630     totalProperty: "results",    // The property which contains the total dataset size (optional)
12631     root: "rows",                // The property which contains an Array of row objects
12632     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12633 }, RecordDef);
12634 </code></pre>
12635  * <p>
12636  * This would consume a JSON file like this:
12637  * <pre><code>
12638 { 'results': 2, 'rows': [
12639     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12640     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12641 }
12642 </code></pre>
12643  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12644  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12645  * paged from the remote server.
12646  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12647  * @cfg {String} root name of the property which contains the Array of row objects.
12648  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12649  * @cfg {Array} fields Array of field definition objects
12650  * @constructor
12651  * Create a new JsonReader
12652  * @param {Object} meta Metadata configuration options
12653  * @param {Object} recordType Either an Array of field definition objects,
12654  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12655  */
12656 Roo.data.JsonReader = function(meta, recordType){
12657     
12658     meta = meta || {};
12659     // set some defaults:
12660     Roo.applyIf(meta, {
12661         totalProperty: 'total',
12662         successProperty : 'success',
12663         root : 'data',
12664         id : 'id'
12665     });
12666     
12667     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12668 };
12669 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12670     
12671     /**
12672      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12673      * Used by Store query builder to append _requestMeta to params.
12674      * 
12675      */
12676     metaFromRemote : false,
12677     /**
12678      * This method is only used by a DataProxy which has retrieved data from a remote server.
12679      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12680      * @return {Object} data A data block which is used by an Roo.data.Store object as
12681      * a cache of Roo.data.Records.
12682      */
12683     read : function(response){
12684         var json = response.responseText;
12685        
12686         var o = /* eval:var:o */ eval("("+json+")");
12687         if(!o) {
12688             throw {message: "JsonReader.read: Json object not found"};
12689         }
12690         
12691         if(o.metaData){
12692             
12693             delete this.ef;
12694             this.metaFromRemote = true;
12695             this.meta = o.metaData;
12696             this.recordType = Roo.data.Record.create(o.metaData.fields);
12697             this.onMetaChange(this.meta, this.recordType, o);
12698         }
12699         return this.readRecords(o);
12700     },
12701
12702     // private function a store will implement
12703     onMetaChange : function(meta, recordType, o){
12704
12705     },
12706
12707     /**
12708          * @ignore
12709          */
12710     simpleAccess: function(obj, subsc) {
12711         return obj[subsc];
12712     },
12713
12714         /**
12715          * @ignore
12716          */
12717     getJsonAccessor: function(){
12718         var re = /[\[\.]/;
12719         return function(expr) {
12720             try {
12721                 return(re.test(expr))
12722                     ? new Function("obj", "return obj." + expr)
12723                     : function(obj){
12724                         return obj[expr];
12725                     };
12726             } catch(e){}
12727             return Roo.emptyFn;
12728         };
12729     }(),
12730
12731     /**
12732      * Create a data block containing Roo.data.Records from an XML document.
12733      * @param {Object} o An object which contains an Array of row objects in the property specified
12734      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12735      * which contains the total size of the dataset.
12736      * @return {Object} data A data block which is used by an Roo.data.Store object as
12737      * a cache of Roo.data.Records.
12738      */
12739     readRecords : function(o){
12740         /**
12741          * After any data loads, the raw JSON data is available for further custom processing.
12742          * @type Object
12743          */
12744         this.o = o;
12745         var s = this.meta, Record = this.recordType,
12746             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12747
12748 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12749         if (!this.ef) {
12750             if(s.totalProperty) {
12751                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12752                 }
12753                 if(s.successProperty) {
12754                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12755                 }
12756                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12757                 if (s.id) {
12758                         var g = this.getJsonAccessor(s.id);
12759                         this.getId = function(rec) {
12760                                 var r = g(rec);  
12761                                 return (r === undefined || r === "") ? null : r;
12762                         };
12763                 } else {
12764                         this.getId = function(){return null;};
12765                 }
12766             this.ef = [];
12767             for(var jj = 0; jj < fl; jj++){
12768                 f = fi[jj];
12769                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12770                 this.ef[jj] = this.getJsonAccessor(map);
12771             }
12772         }
12773
12774         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12775         if(s.totalProperty){
12776             var vt = parseInt(this.getTotal(o), 10);
12777             if(!isNaN(vt)){
12778                 totalRecords = vt;
12779             }
12780         }
12781         if(s.successProperty){
12782             var vs = this.getSuccess(o);
12783             if(vs === false || vs === 'false'){
12784                 success = false;
12785             }
12786         }
12787         var records = [];
12788         for(var i = 0; i < c; i++){
12789                 var n = root[i];
12790             var values = {};
12791             var id = this.getId(n);
12792             for(var j = 0; j < fl; j++){
12793                 f = fi[j];
12794             var v = this.ef[j](n);
12795             if (!f.convert) {
12796                 Roo.log('missing convert for ' + f.name);
12797                 Roo.log(f);
12798                 continue;
12799             }
12800             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12801             }
12802             var record = new Record(values, id);
12803             record.json = n;
12804             records[i] = record;
12805         }
12806         return {
12807             raw : o,
12808             success : success,
12809             records : records,
12810             totalRecords : totalRecords
12811         };
12812     }
12813 });/*
12814  * Based on:
12815  * Ext JS Library 1.1.1
12816  * Copyright(c) 2006-2007, Ext JS, LLC.
12817  *
12818  * Originally Released Under LGPL - original licence link has changed is not relivant.
12819  *
12820  * Fork - LGPL
12821  * <script type="text/javascript">
12822  */
12823
12824 /**
12825  * @class Roo.data.ArrayReader
12826  * @extends Roo.data.DataReader
12827  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12828  * Each element of that Array represents a row of data fields. The
12829  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12830  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12831  * <p>
12832  * Example code:.
12833  * <pre><code>
12834 var RecordDef = Roo.data.Record.create([
12835     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12836     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12837 ]);
12838 var myReader = new Roo.data.ArrayReader({
12839     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12840 }, RecordDef);
12841 </code></pre>
12842  * <p>
12843  * This would consume an Array like this:
12844  * <pre><code>
12845 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12846   </code></pre>
12847  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12848  * @constructor
12849  * Create a new JsonReader
12850  * @param {Object} meta Metadata configuration options.
12851  * @param {Object} recordType Either an Array of field definition objects
12852  * as specified to {@link Roo.data.Record#create},
12853  * or an {@link Roo.data.Record} object
12854  * created using {@link Roo.data.Record#create}.
12855  */
12856 Roo.data.ArrayReader = function(meta, recordType){
12857     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12858 };
12859
12860 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12861     /**
12862      * Create a data block containing Roo.data.Records from an XML document.
12863      * @param {Object} o An Array of row objects which represents the dataset.
12864      * @return {Object} data A data block which is used by an Roo.data.Store object as
12865      * a cache of Roo.data.Records.
12866      */
12867     readRecords : function(o){
12868         var sid = this.meta ? this.meta.id : null;
12869         var recordType = this.recordType, fields = recordType.prototype.fields;
12870         var records = [];
12871         var root = o;
12872             for(var i = 0; i < root.length; i++){
12873                     var n = root[i];
12874                 var values = {};
12875                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12876                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12877                 var f = fields.items[j];
12878                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12879                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12880                 v = f.convert(v);
12881                 values[f.name] = v;
12882             }
12883                 var record = new recordType(values, id);
12884                 record.json = n;
12885                 records[records.length] = record;
12886             }
12887             return {
12888                 records : records,
12889                 totalRecords : records.length
12890             };
12891     }
12892 });/*
12893  * - LGPL
12894  * * 
12895  */
12896
12897 /**
12898  * @class Roo.bootstrap.ComboBox
12899  * @extends Roo.bootstrap.TriggerField
12900  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12901  * @cfg {Boolean} append (true|false) default false
12902  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12903  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12904  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12905  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12906  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12907  * @cfg {Boolean} animate default true
12908  * @cfg {Boolean} emptyResultText only for touch device
12909  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12910  * @cfg {String} emptyTitle default ''
12911  * @constructor
12912  * Create a new ComboBox.
12913  * @param {Object} config Configuration options
12914  */
12915 Roo.bootstrap.ComboBox = function(config){
12916     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12917     this.addEvents({
12918         /**
12919          * @event expand
12920          * Fires when the dropdown list is expanded
12921         * @param {Roo.bootstrap.ComboBox} combo This combo box
12922         */
12923         'expand' : true,
12924         /**
12925          * @event collapse
12926          * Fires when the dropdown list is collapsed
12927         * @param {Roo.bootstrap.ComboBox} combo This combo box
12928         */
12929         'collapse' : true,
12930         /**
12931          * @event beforeselect
12932          * Fires before a list item is selected. Return false to cancel the selection.
12933         * @param {Roo.bootstrap.ComboBox} combo This combo box
12934         * @param {Roo.data.Record} record The data record returned from the underlying store
12935         * @param {Number} index The index of the selected item in the dropdown list
12936         */
12937         'beforeselect' : true,
12938         /**
12939          * @event select
12940          * Fires when a list item is selected
12941         * @param {Roo.bootstrap.ComboBox} combo This combo box
12942         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12943         * @param {Number} index The index of the selected item in the dropdown list
12944         */
12945         'select' : true,
12946         /**
12947          * @event beforequery
12948          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12949          * The event object passed has these properties:
12950         * @param {Roo.bootstrap.ComboBox} combo This combo box
12951         * @param {String} query The query
12952         * @param {Boolean} forceAll true to force "all" query
12953         * @param {Boolean} cancel true to cancel the query
12954         * @param {Object} e The query event object
12955         */
12956         'beforequery': true,
12957          /**
12958          * @event add
12959          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12960         * @param {Roo.bootstrap.ComboBox} combo This combo box
12961         */
12962         'add' : true,
12963         /**
12964          * @event edit
12965          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12966         * @param {Roo.bootstrap.ComboBox} combo This combo box
12967         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12968         */
12969         'edit' : true,
12970         /**
12971          * @event remove
12972          * Fires when the remove value from the combobox array
12973         * @param {Roo.bootstrap.ComboBox} combo This combo box
12974         */
12975         'remove' : true,
12976         /**
12977          * @event afterremove
12978          * Fires when the remove value from the combobox array
12979         * @param {Roo.bootstrap.ComboBox} combo This combo box
12980         */
12981         'afterremove' : true,
12982         /**
12983          * @event specialfilter
12984          * Fires when specialfilter
12985             * @param {Roo.bootstrap.ComboBox} combo This combo box
12986             */
12987         'specialfilter' : true,
12988         /**
12989          * @event tick
12990          * Fires when tick the element
12991             * @param {Roo.bootstrap.ComboBox} combo This combo box
12992             */
12993         'tick' : true,
12994         /**
12995          * @event touchviewdisplay
12996          * Fires when touch view require special display (default is using displayField)
12997             * @param {Roo.bootstrap.ComboBox} combo This combo box
12998             * @param {Object} cfg set html .
12999             */
13000         'touchviewdisplay' : true
13001         
13002     });
13003     
13004     this.item = [];
13005     this.tickItems = [];
13006     
13007     this.selectedIndex = -1;
13008     if(this.mode == 'local'){
13009         if(config.queryDelay === undefined){
13010             this.queryDelay = 10;
13011         }
13012         if(config.minChars === undefined){
13013             this.minChars = 0;
13014         }
13015     }
13016 };
13017
13018 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13019      
13020     /**
13021      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13022      * rendering into an Roo.Editor, defaults to false)
13023      */
13024     /**
13025      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13026      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13027      */
13028     /**
13029      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13030      */
13031     /**
13032      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13033      * the dropdown list (defaults to undefined, with no header element)
13034      */
13035
13036      /**
13037      * @cfg {String/Roo.Template} tpl The template to use to render the output
13038      */
13039      
13040      /**
13041      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13042      */
13043     listWidth: undefined,
13044     /**
13045      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13046      * mode = 'remote' or 'text' if mode = 'local')
13047      */
13048     displayField: undefined,
13049     
13050     /**
13051      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13052      * mode = 'remote' or 'value' if mode = 'local'). 
13053      * Note: use of a valueField requires the user make a selection
13054      * in order for a value to be mapped.
13055      */
13056     valueField: undefined,
13057     /**
13058      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13059      */
13060     modalTitle : '',
13061     
13062     /**
13063      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13064      * field's data value (defaults to the underlying DOM element's name)
13065      */
13066     hiddenName: undefined,
13067     /**
13068      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13069      */
13070     listClass: '',
13071     /**
13072      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13073      */
13074     selectedClass: 'active',
13075     
13076     /**
13077      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13078      */
13079     shadow:'sides',
13080     /**
13081      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13082      * anchor positions (defaults to 'tl-bl')
13083      */
13084     listAlign: 'tl-bl?',
13085     /**
13086      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13087      */
13088     maxHeight: 300,
13089     /**
13090      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13091      * query specified by the allQuery config option (defaults to 'query')
13092      */
13093     triggerAction: 'query',
13094     /**
13095      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13096      * (defaults to 4, does not apply if editable = false)
13097      */
13098     minChars : 4,
13099     /**
13100      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13101      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13102      */
13103     typeAhead: false,
13104     /**
13105      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13106      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13107      */
13108     queryDelay: 500,
13109     /**
13110      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13111      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13112      */
13113     pageSize: 0,
13114     /**
13115      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13116      * when editable = true (defaults to false)
13117      */
13118     selectOnFocus:false,
13119     /**
13120      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13121      */
13122     queryParam: 'query',
13123     /**
13124      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13125      * when mode = 'remote' (defaults to 'Loading...')
13126      */
13127     loadingText: 'Loading...',
13128     /**
13129      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13130      */
13131     resizable: false,
13132     /**
13133      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13134      */
13135     handleHeight : 8,
13136     /**
13137      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13138      * traditional select (defaults to true)
13139      */
13140     editable: true,
13141     /**
13142      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13143      */
13144     allQuery: '',
13145     /**
13146      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13147      */
13148     mode: 'remote',
13149     /**
13150      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13151      * listWidth has a higher value)
13152      */
13153     minListWidth : 70,
13154     /**
13155      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13156      * allow the user to set arbitrary text into the field (defaults to false)
13157      */
13158     forceSelection:false,
13159     /**
13160      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13161      * if typeAhead = true (defaults to 250)
13162      */
13163     typeAheadDelay : 250,
13164     /**
13165      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13166      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13167      */
13168     valueNotFoundText : undefined,
13169     /**
13170      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13171      */
13172     blockFocus : false,
13173     
13174     /**
13175      * @cfg {Boolean} disableClear Disable showing of clear button.
13176      */
13177     disableClear : false,
13178     /**
13179      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13180      */
13181     alwaysQuery : false,
13182     
13183     /**
13184      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13185      */
13186     multiple : false,
13187     
13188     /**
13189      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13190      */
13191     invalidClass : "has-warning",
13192     
13193     /**
13194      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13195      */
13196     validClass : "has-success",
13197     
13198     /**
13199      * @cfg {Boolean} specialFilter (true|false) special filter default false
13200      */
13201     specialFilter : false,
13202     
13203     /**
13204      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13205      */
13206     mobileTouchView : true,
13207     
13208     /**
13209      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13210      */
13211     useNativeIOS : false,
13212     
13213     /**
13214      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13215      */
13216     mobile_restrict_height : false,
13217     
13218     ios_options : false,
13219     
13220     //private
13221     addicon : false,
13222     editicon: false,
13223     
13224     page: 0,
13225     hasQuery: false,
13226     append: false,
13227     loadNext: false,
13228     autoFocus : true,
13229     tickable : false,
13230     btnPosition : 'right',
13231     triggerList : true,
13232     showToggleBtn : true,
13233     animate : true,
13234     emptyResultText: 'Empty',
13235     triggerText : 'Select',
13236     emptyTitle : '',
13237     
13238     // element that contains real text value.. (when hidden is used..)
13239     
13240     getAutoCreate : function()
13241     {   
13242         var cfg = false;
13243         //render
13244         /*
13245          * Render classic select for iso
13246          */
13247         
13248         if(Roo.isIOS && this.useNativeIOS){
13249             cfg = this.getAutoCreateNativeIOS();
13250             return cfg;
13251         }
13252         
13253         /*
13254          * Touch Devices
13255          */
13256         
13257         if(Roo.isTouch && this.mobileTouchView){
13258             cfg = this.getAutoCreateTouchView();
13259             return cfg;;
13260         }
13261         
13262         /*
13263          *  Normal ComboBox
13264          */
13265         if(!this.tickable){
13266             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13267             return cfg;
13268         }
13269         
13270         /*
13271          *  ComboBox with tickable selections
13272          */
13273              
13274         var align = this.labelAlign || this.parentLabelAlign();
13275         
13276         cfg = {
13277             cls : 'form-group roo-combobox-tickable' //input-group
13278         };
13279         
13280         var btn_text_select = '';
13281         var btn_text_done = '';
13282         var btn_text_cancel = '';
13283         
13284         if (this.btn_text_show) {
13285             btn_text_select = 'Select';
13286             btn_text_done = 'Done';
13287             btn_text_cancel = 'Cancel'; 
13288         }
13289         
13290         var buttons = {
13291             tag : 'div',
13292             cls : 'tickable-buttons',
13293             cn : [
13294                 {
13295                     tag : 'button',
13296                     type : 'button',
13297                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13298                     //html : this.triggerText
13299                     html: btn_text_select
13300                 },
13301                 {
13302                     tag : 'button',
13303                     type : 'button',
13304                     name : 'ok',
13305                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13306                     //html : 'Done'
13307                     html: btn_text_done
13308                 },
13309                 {
13310                     tag : 'button',
13311                     type : 'button',
13312                     name : 'cancel',
13313                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13314                     //html : 'Cancel'
13315                     html: btn_text_cancel
13316                 }
13317             ]
13318         };
13319         
13320         if(this.editable){
13321             buttons.cn.unshift({
13322                 tag: 'input',
13323                 cls: 'roo-select2-search-field-input'
13324             });
13325         }
13326         
13327         var _this = this;
13328         
13329         Roo.each(buttons.cn, function(c){
13330             if (_this.size) {
13331                 c.cls += ' btn-' + _this.size;
13332             }
13333
13334             if (_this.disabled) {
13335                 c.disabled = true;
13336             }
13337         });
13338         
13339         var box = {
13340             tag: 'div',
13341             cn: [
13342                 {
13343                     tag: 'input',
13344                     type : 'hidden',
13345                     cls: 'form-hidden-field'
13346                 },
13347                 {
13348                     tag: 'ul',
13349                     cls: 'roo-select2-choices',
13350                     cn:[
13351                         {
13352                             tag: 'li',
13353                             cls: 'roo-select2-search-field',
13354                             cn: [
13355                                 buttons
13356                             ]
13357                         }
13358                     ]
13359                 }
13360             ]
13361         };
13362         
13363         var combobox = {
13364             cls: 'roo-select2-container input-group roo-select2-container-multi',
13365             cn: [
13366                 
13367                 box
13368 //                {
13369 //                    tag: 'ul',
13370 //                    cls: 'typeahead typeahead-long dropdown-menu',
13371 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13372 //                }
13373             ]
13374         };
13375         
13376         if(this.hasFeedback && !this.allowBlank){
13377             
13378             var feedback = {
13379                 tag: 'span',
13380                 cls: 'glyphicon form-control-feedback'
13381             };
13382
13383             combobox.cn.push(feedback);
13384         }
13385         
13386         var indicator = {
13387             tag : 'i',
13388             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13389             tooltip : 'This field is required'
13390         };
13391         if (Roo.bootstrap.version == 4) {
13392             indicator = {
13393                 tag : 'i',
13394                 style : 'display:none'
13395             };
13396         }
13397         if (align ==='left' && this.fieldLabel.length) {
13398             
13399             cfg.cls += ' roo-form-group-label-left row';
13400             
13401             cfg.cn = [
13402                 indicator,
13403                 {
13404                     tag: 'label',
13405                     'for' :  id,
13406                     cls : 'control-label col-form-label',
13407                     html : this.fieldLabel
13408
13409                 },
13410                 {
13411                     cls : "", 
13412                     cn: [
13413                         combobox
13414                     ]
13415                 }
13416
13417             ];
13418             
13419             var labelCfg = cfg.cn[1];
13420             var contentCfg = cfg.cn[2];
13421             
13422
13423             if(this.indicatorpos == 'right'){
13424                 
13425                 cfg.cn = [
13426                     {
13427                         tag: 'label',
13428                         'for' :  id,
13429                         cls : 'control-label col-form-label',
13430                         cn : [
13431                             {
13432                                 tag : 'span',
13433                                 html : this.fieldLabel
13434                             },
13435                             indicator
13436                         ]
13437                     },
13438                     {
13439                         cls : "",
13440                         cn: [
13441                             combobox
13442                         ]
13443                     }
13444
13445                 ];
13446                 
13447                 
13448                 
13449                 labelCfg = cfg.cn[0];
13450                 contentCfg = cfg.cn[1];
13451             
13452             }
13453             
13454             if(this.labelWidth > 12){
13455                 labelCfg.style = "width: " + this.labelWidth + 'px';
13456             }
13457             
13458             if(this.labelWidth < 13 && this.labelmd == 0){
13459                 this.labelmd = this.labelWidth;
13460             }
13461             
13462             if(this.labellg > 0){
13463                 labelCfg.cls += ' col-lg-' + this.labellg;
13464                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13465             }
13466             
13467             if(this.labelmd > 0){
13468                 labelCfg.cls += ' col-md-' + this.labelmd;
13469                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13470             }
13471             
13472             if(this.labelsm > 0){
13473                 labelCfg.cls += ' col-sm-' + this.labelsm;
13474                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13475             }
13476             
13477             if(this.labelxs > 0){
13478                 labelCfg.cls += ' col-xs-' + this.labelxs;
13479                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13480             }
13481                 
13482                 
13483         } else if ( this.fieldLabel.length) {
13484 //                Roo.log(" label");
13485                  cfg.cn = [
13486                    indicator,
13487                     {
13488                         tag: 'label',
13489                         //cls : 'input-group-addon',
13490                         html : this.fieldLabel
13491                     },
13492                     combobox
13493                 ];
13494                 
13495                 if(this.indicatorpos == 'right'){
13496                     cfg.cn = [
13497                         {
13498                             tag: 'label',
13499                             //cls : 'input-group-addon',
13500                             html : this.fieldLabel
13501                         },
13502                         indicator,
13503                         combobox
13504                     ];
13505                     
13506                 }
13507
13508         } else {
13509             
13510 //                Roo.log(" no label && no align");
13511                 cfg = combobox
13512                      
13513                 
13514         }
13515          
13516         var settings=this;
13517         ['xs','sm','md','lg'].map(function(size){
13518             if (settings[size]) {
13519                 cfg.cls += ' col-' + size + '-' + settings[size];
13520             }
13521         });
13522         
13523         return cfg;
13524         
13525     },
13526     
13527     _initEventsCalled : false,
13528     
13529     // private
13530     initEvents: function()
13531     {   
13532         if (this._initEventsCalled) { // as we call render... prevent looping...
13533             return;
13534         }
13535         this._initEventsCalled = true;
13536         
13537         if (!this.store) {
13538             throw "can not find store for combo";
13539         }
13540         
13541         this.indicator = this.indicatorEl();
13542         
13543         this.store = Roo.factory(this.store, Roo.data);
13544         this.store.parent = this;
13545         
13546         // if we are building from html. then this element is so complex, that we can not really
13547         // use the rendered HTML.
13548         // so we have to trash and replace the previous code.
13549         if (Roo.XComponent.build_from_html) {
13550             // remove this element....
13551             var e = this.el.dom, k=0;
13552             while (e ) { e = e.previousSibling;  ++k;}
13553
13554             this.el.remove();
13555             
13556             this.el=false;
13557             this.rendered = false;
13558             
13559             this.render(this.parent().getChildContainer(true), k);
13560         }
13561         
13562         if(Roo.isIOS && this.useNativeIOS){
13563             this.initIOSView();
13564             return;
13565         }
13566         
13567         /*
13568          * Touch Devices
13569          */
13570         
13571         if(Roo.isTouch && this.mobileTouchView){
13572             this.initTouchView();
13573             return;
13574         }
13575         
13576         if(this.tickable){
13577             this.initTickableEvents();
13578             return;
13579         }
13580         
13581         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13582         
13583         if(this.hiddenName){
13584             
13585             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13586             
13587             this.hiddenField.dom.value =
13588                 this.hiddenValue !== undefined ? this.hiddenValue :
13589                 this.value !== undefined ? this.value : '';
13590
13591             // prevent input submission
13592             this.el.dom.removeAttribute('name');
13593             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13594              
13595              
13596         }
13597         //if(Roo.isGecko){
13598         //    this.el.dom.setAttribute('autocomplete', 'off');
13599         //}
13600         
13601         var cls = 'x-combo-list';
13602         
13603         //this.list = new Roo.Layer({
13604         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13605         //});
13606         
13607         var _this = this;
13608         
13609         (function(){
13610             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13611             _this.list.setWidth(lw);
13612         }).defer(100);
13613         
13614         this.list.on('mouseover', this.onViewOver, this);
13615         this.list.on('mousemove', this.onViewMove, this);
13616         this.list.on('scroll', this.onViewScroll, this);
13617         
13618         /*
13619         this.list.swallowEvent('mousewheel');
13620         this.assetHeight = 0;
13621
13622         if(this.title){
13623             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13624             this.assetHeight += this.header.getHeight();
13625         }
13626
13627         this.innerList = this.list.createChild({cls:cls+'-inner'});
13628         this.innerList.on('mouseover', this.onViewOver, this);
13629         this.innerList.on('mousemove', this.onViewMove, this);
13630         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13631         
13632         if(this.allowBlank && !this.pageSize && !this.disableClear){
13633             this.footer = this.list.createChild({cls:cls+'-ft'});
13634             this.pageTb = new Roo.Toolbar(this.footer);
13635            
13636         }
13637         if(this.pageSize){
13638             this.footer = this.list.createChild({cls:cls+'-ft'});
13639             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13640                     {pageSize: this.pageSize});
13641             
13642         }
13643         
13644         if (this.pageTb && this.allowBlank && !this.disableClear) {
13645             var _this = this;
13646             this.pageTb.add(new Roo.Toolbar.Fill(), {
13647                 cls: 'x-btn-icon x-btn-clear',
13648                 text: '&#160;',
13649                 handler: function()
13650                 {
13651                     _this.collapse();
13652                     _this.clearValue();
13653                     _this.onSelect(false, -1);
13654                 }
13655             });
13656         }
13657         if (this.footer) {
13658             this.assetHeight += this.footer.getHeight();
13659         }
13660         */
13661             
13662         if(!this.tpl){
13663             this.tpl = Roo.bootstrap.version == 4 ?
13664                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13665                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13666         }
13667
13668         this.view = new Roo.View(this.list, this.tpl, {
13669             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13670         });
13671         //this.view.wrapEl.setDisplayed(false);
13672         this.view.on('click', this.onViewClick, this);
13673         
13674         
13675         this.store.on('beforeload', this.onBeforeLoad, this);
13676         this.store.on('load', this.onLoad, this);
13677         this.store.on('loadexception', this.onLoadException, this);
13678         /*
13679         if(this.resizable){
13680             this.resizer = new Roo.Resizable(this.list,  {
13681                pinned:true, handles:'se'
13682             });
13683             this.resizer.on('resize', function(r, w, h){
13684                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13685                 this.listWidth = w;
13686                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13687                 this.restrictHeight();
13688             }, this);
13689             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13690         }
13691         */
13692         if(!this.editable){
13693             this.editable = true;
13694             this.setEditable(false);
13695         }
13696         
13697         /*
13698         
13699         if (typeof(this.events.add.listeners) != 'undefined') {
13700             
13701             this.addicon = this.wrap.createChild(
13702                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13703        
13704             this.addicon.on('click', function(e) {
13705                 this.fireEvent('add', this);
13706             }, this);
13707         }
13708         if (typeof(this.events.edit.listeners) != 'undefined') {
13709             
13710             this.editicon = this.wrap.createChild(
13711                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13712             if (this.addicon) {
13713                 this.editicon.setStyle('margin-left', '40px');
13714             }
13715             this.editicon.on('click', function(e) {
13716                 
13717                 // we fire even  if inothing is selected..
13718                 this.fireEvent('edit', this, this.lastData );
13719                 
13720             }, this);
13721         }
13722         */
13723         
13724         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13725             "up" : function(e){
13726                 this.inKeyMode = true;
13727                 this.selectPrev();
13728             },
13729
13730             "down" : function(e){
13731                 if(!this.isExpanded()){
13732                     this.onTriggerClick();
13733                 }else{
13734                     this.inKeyMode = true;
13735                     this.selectNext();
13736                 }
13737             },
13738
13739             "enter" : function(e){
13740 //                this.onViewClick();
13741                 //return true;
13742                 this.collapse();
13743                 
13744                 if(this.fireEvent("specialkey", this, e)){
13745                     this.onViewClick(false);
13746                 }
13747                 
13748                 return true;
13749             },
13750
13751             "esc" : function(e){
13752                 this.collapse();
13753             },
13754
13755             "tab" : function(e){
13756                 this.collapse();
13757                 
13758                 if(this.fireEvent("specialkey", this, e)){
13759                     this.onViewClick(false);
13760                 }
13761                 
13762                 return true;
13763             },
13764
13765             scope : this,
13766
13767             doRelay : function(foo, bar, hname){
13768                 if(hname == 'down' || this.scope.isExpanded()){
13769                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13770                 }
13771                 return true;
13772             },
13773
13774             forceKeyDown: true
13775         });
13776         
13777         
13778         this.queryDelay = Math.max(this.queryDelay || 10,
13779                 this.mode == 'local' ? 10 : 250);
13780         
13781         
13782         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13783         
13784         if(this.typeAhead){
13785             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13786         }
13787         if(this.editable !== false){
13788             this.inputEl().on("keyup", this.onKeyUp, this);
13789         }
13790         if(this.forceSelection){
13791             this.inputEl().on('blur', this.doForce, this);
13792         }
13793         
13794         if(this.multiple){
13795             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13796             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13797         }
13798     },
13799     
13800     initTickableEvents: function()
13801     {   
13802         this.createList();
13803         
13804         if(this.hiddenName){
13805             
13806             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13807             
13808             this.hiddenField.dom.value =
13809                 this.hiddenValue !== undefined ? this.hiddenValue :
13810                 this.value !== undefined ? this.value : '';
13811
13812             // prevent input submission
13813             this.el.dom.removeAttribute('name');
13814             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13815              
13816              
13817         }
13818         
13819 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13820         
13821         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13822         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13823         if(this.triggerList){
13824             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13825         }
13826          
13827         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13828         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13829         
13830         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13831         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13832         
13833         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13834         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13835         
13836         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13837         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13838         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13839         
13840         this.okBtn.hide();
13841         this.cancelBtn.hide();
13842         
13843         var _this = this;
13844         
13845         (function(){
13846             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13847             _this.list.setWidth(lw);
13848         }).defer(100);
13849         
13850         this.list.on('mouseover', this.onViewOver, this);
13851         this.list.on('mousemove', this.onViewMove, this);
13852         
13853         this.list.on('scroll', this.onViewScroll, this);
13854         
13855         if(!this.tpl){
13856             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13857                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13858         }
13859
13860         this.view = new Roo.View(this.list, this.tpl, {
13861             singleSelect:true,
13862             tickable:true,
13863             parent:this,
13864             store: this.store,
13865             selectedClass: this.selectedClass
13866         });
13867         
13868         //this.view.wrapEl.setDisplayed(false);
13869         this.view.on('click', this.onViewClick, this);
13870         
13871         
13872         
13873         this.store.on('beforeload', this.onBeforeLoad, this);
13874         this.store.on('load', this.onLoad, this);
13875         this.store.on('loadexception', this.onLoadException, this);
13876         
13877         if(this.editable){
13878             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13879                 "up" : function(e){
13880                     this.inKeyMode = true;
13881                     this.selectPrev();
13882                 },
13883
13884                 "down" : function(e){
13885                     this.inKeyMode = true;
13886                     this.selectNext();
13887                 },
13888
13889                 "enter" : function(e){
13890                     if(this.fireEvent("specialkey", this, e)){
13891                         this.onViewClick(false);
13892                     }
13893                     
13894                     return true;
13895                 },
13896
13897                 "esc" : function(e){
13898                     this.onTickableFooterButtonClick(e, false, false);
13899                 },
13900
13901                 "tab" : function(e){
13902                     this.fireEvent("specialkey", this, e);
13903                     
13904                     this.onTickableFooterButtonClick(e, false, false);
13905                     
13906                     return true;
13907                 },
13908
13909                 scope : this,
13910
13911                 doRelay : function(e, fn, key){
13912                     if(this.scope.isExpanded()){
13913                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13914                     }
13915                     return true;
13916                 },
13917
13918                 forceKeyDown: true
13919             });
13920         }
13921         
13922         this.queryDelay = Math.max(this.queryDelay || 10,
13923                 this.mode == 'local' ? 10 : 250);
13924         
13925         
13926         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13927         
13928         if(this.typeAhead){
13929             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13930         }
13931         
13932         if(this.editable !== false){
13933             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13934         }
13935         
13936         this.indicator = this.indicatorEl();
13937         
13938         if(this.indicator){
13939             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13940             this.indicator.hide();
13941         }
13942         
13943     },
13944
13945     onDestroy : function(){
13946         if(this.view){
13947             this.view.setStore(null);
13948             this.view.el.removeAllListeners();
13949             this.view.el.remove();
13950             this.view.purgeListeners();
13951         }
13952         if(this.list){
13953             this.list.dom.innerHTML  = '';
13954         }
13955         
13956         if(this.store){
13957             this.store.un('beforeload', this.onBeforeLoad, this);
13958             this.store.un('load', this.onLoad, this);
13959             this.store.un('loadexception', this.onLoadException, this);
13960         }
13961         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13962     },
13963
13964     // private
13965     fireKey : function(e){
13966         if(e.isNavKeyPress() && !this.list.isVisible()){
13967             this.fireEvent("specialkey", this, e);
13968         }
13969     },
13970
13971     // private
13972     onResize: function(w, h){
13973 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13974 //        
13975 //        if(typeof w != 'number'){
13976 //            // we do not handle it!?!?
13977 //            return;
13978 //        }
13979 //        var tw = this.trigger.getWidth();
13980 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13981 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13982 //        var x = w - tw;
13983 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13984 //            
13985 //        //this.trigger.setStyle('left', x+'px');
13986 //        
13987 //        if(this.list && this.listWidth === undefined){
13988 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13989 //            this.list.setWidth(lw);
13990 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13991 //        }
13992         
13993     
13994         
13995     },
13996
13997     /**
13998      * Allow or prevent the user from directly editing the field text.  If false is passed,
13999      * the user will only be able to select from the items defined in the dropdown list.  This method
14000      * is the runtime equivalent of setting the 'editable' config option at config time.
14001      * @param {Boolean} value True to allow the user to directly edit the field text
14002      */
14003     setEditable : function(value){
14004         if(value == this.editable){
14005             return;
14006         }
14007         this.editable = value;
14008         if(!value){
14009             this.inputEl().dom.setAttribute('readOnly', true);
14010             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14011             this.inputEl().addClass('x-combo-noedit');
14012         }else{
14013             this.inputEl().dom.setAttribute('readOnly', false);
14014             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14015             this.inputEl().removeClass('x-combo-noedit');
14016         }
14017     },
14018
14019     // private
14020     
14021     onBeforeLoad : function(combo,opts){
14022         if(!this.hasFocus){
14023             return;
14024         }
14025          if (!opts.add) {
14026             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14027          }
14028         this.restrictHeight();
14029         this.selectedIndex = -1;
14030     },
14031
14032     // private
14033     onLoad : function(){
14034         
14035         this.hasQuery = false;
14036         
14037         if(!this.hasFocus){
14038             return;
14039         }
14040         
14041         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14042             this.loading.hide();
14043         }
14044         
14045         if(this.store.getCount() > 0){
14046             
14047             this.expand();
14048             this.restrictHeight();
14049             if(this.lastQuery == this.allQuery){
14050                 if(this.editable && !this.tickable){
14051                     this.inputEl().dom.select();
14052                 }
14053                 
14054                 if(
14055                     !this.selectByValue(this.value, true) &&
14056                     this.autoFocus && 
14057                     (
14058                         !this.store.lastOptions ||
14059                         typeof(this.store.lastOptions.add) == 'undefined' || 
14060                         this.store.lastOptions.add != true
14061                     )
14062                 ){
14063                     this.select(0, true);
14064                 }
14065             }else{
14066                 if(this.autoFocus){
14067                     this.selectNext();
14068                 }
14069                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14070                     this.taTask.delay(this.typeAheadDelay);
14071                 }
14072             }
14073         }else{
14074             this.onEmptyResults();
14075         }
14076         
14077         //this.el.focus();
14078     },
14079     // private
14080     onLoadException : function()
14081     {
14082         this.hasQuery = false;
14083         
14084         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14085             this.loading.hide();
14086         }
14087         
14088         if(this.tickable && this.editable){
14089             return;
14090         }
14091         
14092         this.collapse();
14093         // only causes errors at present
14094         //Roo.log(this.store.reader.jsonData);
14095         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14096             // fixme
14097             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14098         //}
14099         
14100         
14101     },
14102     // private
14103     onTypeAhead : function(){
14104         if(this.store.getCount() > 0){
14105             var r = this.store.getAt(0);
14106             var newValue = r.data[this.displayField];
14107             var len = newValue.length;
14108             var selStart = this.getRawValue().length;
14109             
14110             if(selStart != len){
14111                 this.setRawValue(newValue);
14112                 this.selectText(selStart, newValue.length);
14113             }
14114         }
14115     },
14116
14117     // private
14118     onSelect : function(record, index){
14119         
14120         if(this.fireEvent('beforeselect', this, record, index) !== false){
14121         
14122             this.setFromData(index > -1 ? record.data : false);
14123             
14124             this.collapse();
14125             this.fireEvent('select', this, record, index);
14126         }
14127     },
14128
14129     /**
14130      * Returns the currently selected field value or empty string if no value is set.
14131      * @return {String} value The selected value
14132      */
14133     getValue : function()
14134     {
14135         if(Roo.isIOS && this.useNativeIOS){
14136             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14137         }
14138         
14139         if(this.multiple){
14140             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14141         }
14142         
14143         if(this.valueField){
14144             return typeof this.value != 'undefined' ? this.value : '';
14145         }else{
14146             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14147         }
14148     },
14149     
14150     getRawValue : function()
14151     {
14152         if(Roo.isIOS && this.useNativeIOS){
14153             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14154         }
14155         
14156         var v = this.inputEl().getValue();
14157         
14158         return v;
14159     },
14160
14161     /**
14162      * Clears any text/value currently set in the field
14163      */
14164     clearValue : function(){
14165         
14166         if(this.hiddenField){
14167             this.hiddenField.dom.value = '';
14168         }
14169         this.value = '';
14170         this.setRawValue('');
14171         this.lastSelectionText = '';
14172         this.lastData = false;
14173         
14174         var close = this.closeTriggerEl();
14175         
14176         if(close){
14177             close.hide();
14178         }
14179         
14180         this.validate();
14181         
14182     },
14183
14184     /**
14185      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14186      * will be displayed in the field.  If the value does not match the data value of an existing item,
14187      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14188      * Otherwise the field will be blank (although the value will still be set).
14189      * @param {String} value The value to match
14190      */
14191     setValue : function(v)
14192     {
14193         if(Roo.isIOS && this.useNativeIOS){
14194             this.setIOSValue(v);
14195             return;
14196         }
14197         
14198         if(this.multiple){
14199             this.syncValue();
14200             return;
14201         }
14202         
14203         var text = v;
14204         if(this.valueField){
14205             var r = this.findRecord(this.valueField, v);
14206             if(r){
14207                 text = r.data[this.displayField];
14208             }else if(this.valueNotFoundText !== undefined){
14209                 text = this.valueNotFoundText;
14210             }
14211         }
14212         this.lastSelectionText = text;
14213         if(this.hiddenField){
14214             this.hiddenField.dom.value = v;
14215         }
14216         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14217         this.value = v;
14218         
14219         var close = this.closeTriggerEl();
14220         
14221         if(close){
14222             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14223         }
14224         
14225         this.validate();
14226     },
14227     /**
14228      * @property {Object} the last set data for the element
14229      */
14230     
14231     lastData : false,
14232     /**
14233      * Sets the value of the field based on a object which is related to the record format for the store.
14234      * @param {Object} value the value to set as. or false on reset?
14235      */
14236     setFromData : function(o){
14237         
14238         if(this.multiple){
14239             this.addItem(o);
14240             return;
14241         }
14242             
14243         var dv = ''; // display value
14244         var vv = ''; // value value..
14245         this.lastData = o;
14246         if (this.displayField) {
14247             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14248         } else {
14249             // this is an error condition!!!
14250             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14251         }
14252         
14253         if(this.valueField){
14254             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14255         }
14256         
14257         var close = this.closeTriggerEl();
14258         
14259         if(close){
14260             if(dv.length || vv * 1 > 0){
14261                 close.show() ;
14262                 this.blockFocus=true;
14263             } else {
14264                 close.hide();
14265             }             
14266         }
14267         
14268         if(this.hiddenField){
14269             this.hiddenField.dom.value = vv;
14270             
14271             this.lastSelectionText = dv;
14272             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14273             this.value = vv;
14274             return;
14275         }
14276         // no hidden field.. - we store the value in 'value', but still display
14277         // display field!!!!
14278         this.lastSelectionText = dv;
14279         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14280         this.value = vv;
14281         
14282         
14283         
14284     },
14285     // private
14286     reset : function(){
14287         // overridden so that last data is reset..
14288         
14289         if(this.multiple){
14290             this.clearItem();
14291             return;
14292         }
14293         
14294         this.setValue(this.originalValue);
14295         //this.clearInvalid();
14296         this.lastData = false;
14297         if (this.view) {
14298             this.view.clearSelections();
14299         }
14300         
14301         this.validate();
14302     },
14303     // private
14304     findRecord : function(prop, value){
14305         var record;
14306         if(this.store.getCount() > 0){
14307             this.store.each(function(r){
14308                 if(r.data[prop] == value){
14309                     record = r;
14310                     return false;
14311                 }
14312                 return true;
14313             });
14314         }
14315         return record;
14316     },
14317     
14318     getName: function()
14319     {
14320         // returns hidden if it's set..
14321         if (!this.rendered) {return ''};
14322         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14323         
14324     },
14325     // private
14326     onViewMove : function(e, t){
14327         this.inKeyMode = false;
14328     },
14329
14330     // private
14331     onViewOver : function(e, t){
14332         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14333             return;
14334         }
14335         var item = this.view.findItemFromChild(t);
14336         
14337         if(item){
14338             var index = this.view.indexOf(item);
14339             this.select(index, false);
14340         }
14341     },
14342
14343     // private
14344     onViewClick : function(view, doFocus, el, e)
14345     {
14346         var index = this.view.getSelectedIndexes()[0];
14347         
14348         var r = this.store.getAt(index);
14349         
14350         if(this.tickable){
14351             
14352             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14353                 return;
14354             }
14355             
14356             var rm = false;
14357             var _this = this;
14358             
14359             Roo.each(this.tickItems, function(v,k){
14360                 
14361                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14362                     Roo.log(v);
14363                     _this.tickItems.splice(k, 1);
14364                     
14365                     if(typeof(e) == 'undefined' && view == false){
14366                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14367                     }
14368                     
14369                     rm = true;
14370                     return;
14371                 }
14372             });
14373             
14374             if(rm){
14375                 return;
14376             }
14377             
14378             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14379                 this.tickItems.push(r.data);
14380             }
14381             
14382             if(typeof(e) == 'undefined' && view == false){
14383                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14384             }
14385                     
14386             return;
14387         }
14388         
14389         if(r){
14390             this.onSelect(r, index);
14391         }
14392         if(doFocus !== false && !this.blockFocus){
14393             this.inputEl().focus();
14394         }
14395     },
14396
14397     // private
14398     restrictHeight : function(){
14399         //this.innerList.dom.style.height = '';
14400         //var inner = this.innerList.dom;
14401         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14402         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14403         //this.list.beginUpdate();
14404         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14405         this.list.alignTo(this.inputEl(), this.listAlign);
14406         this.list.alignTo(this.inputEl(), this.listAlign);
14407         //this.list.endUpdate();
14408     },
14409
14410     // private
14411     onEmptyResults : function(){
14412         
14413         if(this.tickable && this.editable){
14414             this.hasFocus = false;
14415             this.restrictHeight();
14416             return;
14417         }
14418         
14419         this.collapse();
14420     },
14421
14422     /**
14423      * Returns true if the dropdown list is expanded, else false.
14424      */
14425     isExpanded : function(){
14426         return this.list.isVisible();
14427     },
14428
14429     /**
14430      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14431      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14432      * @param {String} value The data value of the item to select
14433      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14434      * selected item if it is not currently in view (defaults to true)
14435      * @return {Boolean} True if the value matched an item in the list, else false
14436      */
14437     selectByValue : function(v, scrollIntoView){
14438         if(v !== undefined && v !== null){
14439             var r = this.findRecord(this.valueField || this.displayField, v);
14440             if(r){
14441                 this.select(this.store.indexOf(r), scrollIntoView);
14442                 return true;
14443             }
14444         }
14445         return false;
14446     },
14447
14448     /**
14449      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14450      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14451      * @param {Number} index The zero-based index of the list item to select
14452      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14453      * selected item if it is not currently in view (defaults to true)
14454      */
14455     select : function(index, scrollIntoView){
14456         this.selectedIndex = index;
14457         this.view.select(index);
14458         if(scrollIntoView !== false){
14459             var el = this.view.getNode(index);
14460             /*
14461              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14462              */
14463             if(el){
14464                 this.list.scrollChildIntoView(el, false);
14465             }
14466         }
14467     },
14468
14469     // private
14470     selectNext : function(){
14471         var ct = this.store.getCount();
14472         if(ct > 0){
14473             if(this.selectedIndex == -1){
14474                 this.select(0);
14475             }else if(this.selectedIndex < ct-1){
14476                 this.select(this.selectedIndex+1);
14477             }
14478         }
14479     },
14480
14481     // private
14482     selectPrev : function(){
14483         var ct = this.store.getCount();
14484         if(ct > 0){
14485             if(this.selectedIndex == -1){
14486                 this.select(0);
14487             }else if(this.selectedIndex != 0){
14488                 this.select(this.selectedIndex-1);
14489             }
14490         }
14491     },
14492
14493     // private
14494     onKeyUp : function(e){
14495         if(this.editable !== false && !e.isSpecialKey()){
14496             this.lastKey = e.getKey();
14497             this.dqTask.delay(this.queryDelay);
14498         }
14499     },
14500
14501     // private
14502     validateBlur : function(){
14503         return !this.list || !this.list.isVisible();   
14504     },
14505
14506     // private
14507     initQuery : function(){
14508         
14509         var v = this.getRawValue();
14510         
14511         if(this.tickable && this.editable){
14512             v = this.tickableInputEl().getValue();
14513         }
14514         
14515         this.doQuery(v);
14516     },
14517
14518     // private
14519     doForce : function(){
14520         if(this.inputEl().dom.value.length > 0){
14521             this.inputEl().dom.value =
14522                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14523              
14524         }
14525     },
14526
14527     /**
14528      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14529      * query allowing the query action to be canceled if needed.
14530      * @param {String} query The SQL query to execute
14531      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14532      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14533      * saved in the current store (defaults to false)
14534      */
14535     doQuery : function(q, forceAll){
14536         
14537         if(q === undefined || q === null){
14538             q = '';
14539         }
14540         var qe = {
14541             query: q,
14542             forceAll: forceAll,
14543             combo: this,
14544             cancel:false
14545         };
14546         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14547             return false;
14548         }
14549         q = qe.query;
14550         
14551         forceAll = qe.forceAll;
14552         if(forceAll === true || (q.length >= this.minChars)){
14553             
14554             this.hasQuery = true;
14555             
14556             if(this.lastQuery != q || this.alwaysQuery){
14557                 this.lastQuery = q;
14558                 if(this.mode == 'local'){
14559                     this.selectedIndex = -1;
14560                     if(forceAll){
14561                         this.store.clearFilter();
14562                     }else{
14563                         
14564                         if(this.specialFilter){
14565                             this.fireEvent('specialfilter', this);
14566                             this.onLoad();
14567                             return;
14568                         }
14569                         
14570                         this.store.filter(this.displayField, q);
14571                     }
14572                     
14573                     this.store.fireEvent("datachanged", this.store);
14574                     
14575                     this.onLoad();
14576                     
14577                     
14578                 }else{
14579                     
14580                     this.store.baseParams[this.queryParam] = q;
14581                     
14582                     var options = {params : this.getParams(q)};
14583                     
14584                     if(this.loadNext){
14585                         options.add = true;
14586                         options.params.start = this.page * this.pageSize;
14587                     }
14588                     
14589                     this.store.load(options);
14590                     
14591                     /*
14592                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14593                      *  we should expand the list on onLoad
14594                      *  so command out it
14595                      */
14596 //                    this.expand();
14597                 }
14598             }else{
14599                 this.selectedIndex = -1;
14600                 this.onLoad();   
14601             }
14602         }
14603         
14604         this.loadNext = false;
14605     },
14606     
14607     // private
14608     getParams : function(q){
14609         var p = {};
14610         //p[this.queryParam] = q;
14611         
14612         if(this.pageSize){
14613             p.start = 0;
14614             p.limit = this.pageSize;
14615         }
14616         return p;
14617     },
14618
14619     /**
14620      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14621      */
14622     collapse : function(){
14623         if(!this.isExpanded()){
14624             return;
14625         }
14626         
14627         this.list.hide();
14628         
14629         this.hasFocus = false;
14630         
14631         if(this.tickable){
14632             this.okBtn.hide();
14633             this.cancelBtn.hide();
14634             this.trigger.show();
14635             
14636             if(this.editable){
14637                 this.tickableInputEl().dom.value = '';
14638                 this.tickableInputEl().blur();
14639             }
14640             
14641         }
14642         
14643         Roo.get(document).un('mousedown', this.collapseIf, this);
14644         Roo.get(document).un('mousewheel', this.collapseIf, this);
14645         if (!this.editable) {
14646             Roo.get(document).un('keydown', this.listKeyPress, this);
14647         }
14648         this.fireEvent('collapse', this);
14649         
14650         this.validate();
14651     },
14652
14653     // private
14654     collapseIf : function(e){
14655         var in_combo  = e.within(this.el);
14656         var in_list =  e.within(this.list);
14657         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14658         
14659         if (in_combo || in_list || is_list) {
14660             //e.stopPropagation();
14661             return;
14662         }
14663         
14664         if(this.tickable){
14665             this.onTickableFooterButtonClick(e, false, false);
14666         }
14667
14668         this.collapse();
14669         
14670     },
14671
14672     /**
14673      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14674      */
14675     expand : function(){
14676        
14677         if(this.isExpanded() || !this.hasFocus){
14678             return;
14679         }
14680         
14681         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14682         this.list.setWidth(lw);
14683         
14684         Roo.log('expand');
14685         
14686         this.list.show();
14687         
14688         this.restrictHeight();
14689         
14690         if(this.tickable){
14691             
14692             this.tickItems = Roo.apply([], this.item);
14693             
14694             this.okBtn.show();
14695             this.cancelBtn.show();
14696             this.trigger.hide();
14697             
14698             if(this.editable){
14699                 this.tickableInputEl().focus();
14700             }
14701             
14702         }
14703         
14704         Roo.get(document).on('mousedown', this.collapseIf, this);
14705         Roo.get(document).on('mousewheel', this.collapseIf, this);
14706         if (!this.editable) {
14707             Roo.get(document).on('keydown', this.listKeyPress, this);
14708         }
14709         
14710         this.fireEvent('expand', this);
14711     },
14712
14713     // private
14714     // Implements the default empty TriggerField.onTriggerClick function
14715     onTriggerClick : function(e)
14716     {
14717         Roo.log('trigger click');
14718         
14719         if(this.disabled || !this.triggerList){
14720             return;
14721         }
14722         
14723         this.page = 0;
14724         this.loadNext = false;
14725         
14726         if(this.isExpanded()){
14727             this.collapse();
14728             if (!this.blockFocus) {
14729                 this.inputEl().focus();
14730             }
14731             
14732         }else {
14733             this.hasFocus = true;
14734             if(this.triggerAction == 'all') {
14735                 this.doQuery(this.allQuery, true);
14736             } else {
14737                 this.doQuery(this.getRawValue());
14738             }
14739             if (!this.blockFocus) {
14740                 this.inputEl().focus();
14741             }
14742         }
14743     },
14744     
14745     onTickableTriggerClick : function(e)
14746     {
14747         if(this.disabled){
14748             return;
14749         }
14750         
14751         this.page = 0;
14752         this.loadNext = false;
14753         this.hasFocus = true;
14754         
14755         if(this.triggerAction == 'all') {
14756             this.doQuery(this.allQuery, true);
14757         } else {
14758             this.doQuery(this.getRawValue());
14759         }
14760     },
14761     
14762     onSearchFieldClick : function(e)
14763     {
14764         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14765             this.onTickableFooterButtonClick(e, false, false);
14766             return;
14767         }
14768         
14769         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14770             return;
14771         }
14772         
14773         this.page = 0;
14774         this.loadNext = false;
14775         this.hasFocus = true;
14776         
14777         if(this.triggerAction == 'all') {
14778             this.doQuery(this.allQuery, true);
14779         } else {
14780             this.doQuery(this.getRawValue());
14781         }
14782     },
14783     
14784     listKeyPress : function(e)
14785     {
14786         //Roo.log('listkeypress');
14787         // scroll to first matching element based on key pres..
14788         if (e.isSpecialKey()) {
14789             return false;
14790         }
14791         var k = String.fromCharCode(e.getKey()).toUpperCase();
14792         //Roo.log(k);
14793         var match  = false;
14794         var csel = this.view.getSelectedNodes();
14795         var cselitem = false;
14796         if (csel.length) {
14797             var ix = this.view.indexOf(csel[0]);
14798             cselitem  = this.store.getAt(ix);
14799             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14800                 cselitem = false;
14801             }
14802             
14803         }
14804         
14805         this.store.each(function(v) { 
14806             if (cselitem) {
14807                 // start at existing selection.
14808                 if (cselitem.id == v.id) {
14809                     cselitem = false;
14810                 }
14811                 return true;
14812             }
14813                 
14814             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14815                 match = this.store.indexOf(v);
14816                 return false;
14817             }
14818             return true;
14819         }, this);
14820         
14821         if (match === false) {
14822             return true; // no more action?
14823         }
14824         // scroll to?
14825         this.view.select(match);
14826         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14827         sn.scrollIntoView(sn.dom.parentNode, false);
14828     },
14829     
14830     onViewScroll : function(e, t){
14831         
14832         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){
14833             return;
14834         }
14835         
14836         this.hasQuery = true;
14837         
14838         this.loading = this.list.select('.loading', true).first();
14839         
14840         if(this.loading === null){
14841             this.list.createChild({
14842                 tag: 'div',
14843                 cls: 'loading roo-select2-more-results roo-select2-active',
14844                 html: 'Loading more results...'
14845             });
14846             
14847             this.loading = this.list.select('.loading', true).first();
14848             
14849             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14850             
14851             this.loading.hide();
14852         }
14853         
14854         this.loading.show();
14855         
14856         var _combo = this;
14857         
14858         this.page++;
14859         this.loadNext = true;
14860         
14861         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14862         
14863         return;
14864     },
14865     
14866     addItem : function(o)
14867     {   
14868         var dv = ''; // display value
14869         
14870         if (this.displayField) {
14871             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14872         } else {
14873             // this is an error condition!!!
14874             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14875         }
14876         
14877         if(!dv.length){
14878             return;
14879         }
14880         
14881         var choice = this.choices.createChild({
14882             tag: 'li',
14883             cls: 'roo-select2-search-choice',
14884             cn: [
14885                 {
14886                     tag: 'div',
14887                     html: dv
14888                 },
14889                 {
14890                     tag: 'a',
14891                     href: '#',
14892                     cls: 'roo-select2-search-choice-close fa fa-times',
14893                     tabindex: '-1'
14894                 }
14895             ]
14896             
14897         }, this.searchField);
14898         
14899         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14900         
14901         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14902         
14903         this.item.push(o);
14904         
14905         this.lastData = o;
14906         
14907         this.syncValue();
14908         
14909         this.inputEl().dom.value = '';
14910         
14911         this.validate();
14912     },
14913     
14914     onRemoveItem : function(e, _self, o)
14915     {
14916         e.preventDefault();
14917         
14918         this.lastItem = Roo.apply([], this.item);
14919         
14920         var index = this.item.indexOf(o.data) * 1;
14921         
14922         if( index < 0){
14923             Roo.log('not this item?!');
14924             return;
14925         }
14926         
14927         this.item.splice(index, 1);
14928         o.item.remove();
14929         
14930         this.syncValue();
14931         
14932         this.fireEvent('remove', this, e);
14933         
14934         this.validate();
14935         
14936     },
14937     
14938     syncValue : function()
14939     {
14940         if(!this.item.length){
14941             this.clearValue();
14942             return;
14943         }
14944             
14945         var value = [];
14946         var _this = this;
14947         Roo.each(this.item, function(i){
14948             if(_this.valueField){
14949                 value.push(i[_this.valueField]);
14950                 return;
14951             }
14952
14953             value.push(i);
14954         });
14955
14956         this.value = value.join(',');
14957
14958         if(this.hiddenField){
14959             this.hiddenField.dom.value = this.value;
14960         }
14961         
14962         this.store.fireEvent("datachanged", this.store);
14963         
14964         this.validate();
14965     },
14966     
14967     clearItem : function()
14968     {
14969         if(!this.multiple){
14970             return;
14971         }
14972         
14973         this.item = [];
14974         
14975         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14976            c.remove();
14977         });
14978         
14979         this.syncValue();
14980         
14981         this.validate();
14982         
14983         if(this.tickable && !Roo.isTouch){
14984             this.view.refresh();
14985         }
14986     },
14987     
14988     inputEl: function ()
14989     {
14990         if(Roo.isIOS && this.useNativeIOS){
14991             return this.el.select('select.roo-ios-select', true).first();
14992         }
14993         
14994         if(Roo.isTouch && this.mobileTouchView){
14995             return this.el.select('input.form-control',true).first();
14996         }
14997         
14998         if(this.tickable){
14999             return this.searchField;
15000         }
15001         
15002         return this.el.select('input.form-control',true).first();
15003     },
15004     
15005     onTickableFooterButtonClick : function(e, btn, el)
15006     {
15007         e.preventDefault();
15008         
15009         this.lastItem = Roo.apply([], this.item);
15010         
15011         if(btn && btn.name == 'cancel'){
15012             this.tickItems = Roo.apply([], this.item);
15013             this.collapse();
15014             return;
15015         }
15016         
15017         this.clearItem();
15018         
15019         var _this = this;
15020         
15021         Roo.each(this.tickItems, function(o){
15022             _this.addItem(o);
15023         });
15024         
15025         this.collapse();
15026         
15027     },
15028     
15029     validate : function()
15030     {
15031         if(this.getVisibilityEl().hasClass('hidden')){
15032             return true;
15033         }
15034         
15035         var v = this.getRawValue();
15036         
15037         if(this.multiple){
15038             v = this.getValue();
15039         }
15040         
15041         if(this.disabled || this.allowBlank || v.length){
15042             this.markValid();
15043             return true;
15044         }
15045         
15046         this.markInvalid();
15047         return false;
15048     },
15049     
15050     tickableInputEl : function()
15051     {
15052         if(!this.tickable || !this.editable){
15053             return this.inputEl();
15054         }
15055         
15056         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15057     },
15058     
15059     
15060     getAutoCreateTouchView : function()
15061     {
15062         var id = Roo.id();
15063         
15064         var cfg = {
15065             cls: 'form-group' //input-group
15066         };
15067         
15068         var input =  {
15069             tag: 'input',
15070             id : id,
15071             type : this.inputType,
15072             cls : 'form-control x-combo-noedit',
15073             autocomplete: 'new-password',
15074             placeholder : this.placeholder || '',
15075             readonly : true
15076         };
15077         
15078         if (this.name) {
15079             input.name = this.name;
15080         }
15081         
15082         if (this.size) {
15083             input.cls += ' input-' + this.size;
15084         }
15085         
15086         if (this.disabled) {
15087             input.disabled = true;
15088         }
15089         
15090         var inputblock = {
15091             cls : '',
15092             cn : [
15093                 input
15094             ]
15095         };
15096         
15097         if(this.before){
15098             inputblock.cls += ' input-group';
15099             
15100             inputblock.cn.unshift({
15101                 tag :'span',
15102                 cls : 'input-group-addon input-group-prepend input-group-text',
15103                 html : this.before
15104             });
15105         }
15106         
15107         if(this.removable && !this.multiple){
15108             inputblock.cls += ' roo-removable';
15109             
15110             inputblock.cn.push({
15111                 tag: 'button',
15112                 html : 'x',
15113                 cls : 'roo-combo-removable-btn close'
15114             });
15115         }
15116
15117         if(this.hasFeedback && !this.allowBlank){
15118             
15119             inputblock.cls += ' has-feedback';
15120             
15121             inputblock.cn.push({
15122                 tag: 'span',
15123                 cls: 'glyphicon form-control-feedback'
15124             });
15125             
15126         }
15127         
15128         if (this.after) {
15129             
15130             inputblock.cls += (this.before) ? '' : ' input-group';
15131             
15132             inputblock.cn.push({
15133                 tag :'span',
15134                 cls : 'input-group-addon input-group-append input-group-text',
15135                 html : this.after
15136             });
15137         }
15138
15139         
15140         var ibwrap = inputblock;
15141         
15142         if(this.multiple){
15143             ibwrap = {
15144                 tag: 'ul',
15145                 cls: 'roo-select2-choices',
15146                 cn:[
15147                     {
15148                         tag: 'li',
15149                         cls: 'roo-select2-search-field',
15150                         cn: [
15151
15152                             inputblock
15153                         ]
15154                     }
15155                 ]
15156             };
15157         
15158             
15159         }
15160         
15161         var combobox = {
15162             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15163             cn: [
15164                 {
15165                     tag: 'input',
15166                     type : 'hidden',
15167                     cls: 'form-hidden-field'
15168                 },
15169                 ibwrap
15170             ]
15171         };
15172         
15173         if(!this.multiple && this.showToggleBtn){
15174             
15175             var caret = {
15176                         tag: 'span',
15177                         cls: 'caret'
15178             };
15179             
15180             if (this.caret != false) {
15181                 caret = {
15182                      tag: 'i',
15183                      cls: 'fa fa-' + this.caret
15184                 };
15185                 
15186             }
15187             
15188             combobox.cn.push({
15189                 tag :'span',
15190                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15191                 cn : [
15192                     caret,
15193                     {
15194                         tag: 'span',
15195                         cls: 'combobox-clear',
15196                         cn  : [
15197                             {
15198                                 tag : 'i',
15199                                 cls: 'icon-remove'
15200                             }
15201                         ]
15202                     }
15203                 ]
15204
15205             })
15206         }
15207         
15208         if(this.multiple){
15209             combobox.cls += ' roo-select2-container-multi';
15210         }
15211         
15212         var align = this.labelAlign || this.parentLabelAlign();
15213         
15214         if (align ==='left' && this.fieldLabel.length) {
15215
15216             cfg.cn = [
15217                 {
15218                    tag : 'i',
15219                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15220                    tooltip : 'This field is required'
15221                 },
15222                 {
15223                     tag: 'label',
15224                     cls : 'control-label col-form-label',
15225                     html : this.fieldLabel
15226
15227                 },
15228                 {
15229                     cls : '', 
15230                     cn: [
15231                         combobox
15232                     ]
15233                 }
15234             ];
15235             
15236             var labelCfg = cfg.cn[1];
15237             var contentCfg = cfg.cn[2];
15238             
15239
15240             if(this.indicatorpos == 'right'){
15241                 cfg.cn = [
15242                     {
15243                         tag: 'label',
15244                         'for' :  id,
15245                         cls : 'control-label col-form-label',
15246                         cn : [
15247                             {
15248                                 tag : 'span',
15249                                 html : this.fieldLabel
15250                             },
15251                             {
15252                                 tag : 'i',
15253                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15254                                 tooltip : 'This field is required'
15255                             }
15256                         ]
15257                     },
15258                     {
15259                         cls : "",
15260                         cn: [
15261                             combobox
15262                         ]
15263                     }
15264
15265                 ];
15266                 
15267                 labelCfg = cfg.cn[0];
15268                 contentCfg = cfg.cn[1];
15269             }
15270             
15271            
15272             
15273             if(this.labelWidth > 12){
15274                 labelCfg.style = "width: " + this.labelWidth + 'px';
15275             }
15276             
15277             if(this.labelWidth < 13 && this.labelmd == 0){
15278                 this.labelmd = this.labelWidth;
15279             }
15280             
15281             if(this.labellg > 0){
15282                 labelCfg.cls += ' col-lg-' + this.labellg;
15283                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15284             }
15285             
15286             if(this.labelmd > 0){
15287                 labelCfg.cls += ' col-md-' + this.labelmd;
15288                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15289             }
15290             
15291             if(this.labelsm > 0){
15292                 labelCfg.cls += ' col-sm-' + this.labelsm;
15293                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15294             }
15295             
15296             if(this.labelxs > 0){
15297                 labelCfg.cls += ' col-xs-' + this.labelxs;
15298                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15299             }
15300                 
15301                 
15302         } else if ( this.fieldLabel.length) {
15303             cfg.cn = [
15304                 {
15305                    tag : 'i',
15306                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15307                    tooltip : 'This field is required'
15308                 },
15309                 {
15310                     tag: 'label',
15311                     cls : 'control-label',
15312                     html : this.fieldLabel
15313
15314                 },
15315                 {
15316                     cls : '', 
15317                     cn: [
15318                         combobox
15319                     ]
15320                 }
15321             ];
15322             
15323             if(this.indicatorpos == 'right'){
15324                 cfg.cn = [
15325                     {
15326                         tag: 'label',
15327                         cls : 'control-label',
15328                         html : this.fieldLabel,
15329                         cn : [
15330                             {
15331                                tag : 'i',
15332                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15333                                tooltip : 'This field is required'
15334                             }
15335                         ]
15336                     },
15337                     {
15338                         cls : '', 
15339                         cn: [
15340                             combobox
15341                         ]
15342                     }
15343                 ];
15344             }
15345         } else {
15346             cfg.cn = combobox;    
15347         }
15348         
15349         
15350         var settings = this;
15351         
15352         ['xs','sm','md','lg'].map(function(size){
15353             if (settings[size]) {
15354                 cfg.cls += ' col-' + size + '-' + settings[size];
15355             }
15356         });
15357         
15358         return cfg;
15359     },
15360     
15361     initTouchView : function()
15362     {
15363         this.renderTouchView();
15364         
15365         this.touchViewEl.on('scroll', function(){
15366             this.el.dom.scrollTop = 0;
15367         }, this);
15368         
15369         this.originalValue = this.getValue();
15370         
15371         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15372         
15373         this.inputEl().on("click", this.showTouchView, this);
15374         if (this.triggerEl) {
15375             this.triggerEl.on("click", this.showTouchView, this);
15376         }
15377         
15378         
15379         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15380         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15381         
15382         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15383         
15384         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15385         this.store.on('load', this.onTouchViewLoad, this);
15386         this.store.on('loadexception', this.onTouchViewLoadException, this);
15387         
15388         if(this.hiddenName){
15389             
15390             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15391             
15392             this.hiddenField.dom.value =
15393                 this.hiddenValue !== undefined ? this.hiddenValue :
15394                 this.value !== undefined ? this.value : '';
15395         
15396             this.el.dom.removeAttribute('name');
15397             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15398         }
15399         
15400         if(this.multiple){
15401             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15402             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15403         }
15404         
15405         if(this.removable && !this.multiple){
15406             var close = this.closeTriggerEl();
15407             if(close){
15408                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15409                 close.on('click', this.removeBtnClick, this, close);
15410             }
15411         }
15412         /*
15413          * fix the bug in Safari iOS8
15414          */
15415         this.inputEl().on("focus", function(e){
15416             document.activeElement.blur();
15417         }, this);
15418         
15419         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15420         
15421         return;
15422         
15423         
15424     },
15425     
15426     renderTouchView : function()
15427     {
15428         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15429         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15430         
15431         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15432         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15433         
15434         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15435         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15436         this.touchViewBodyEl.setStyle('overflow', 'auto');
15437         
15438         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15439         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15440         
15441         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15442         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15443         
15444     },
15445     
15446     showTouchView : function()
15447     {
15448         if(this.disabled){
15449             return;
15450         }
15451         
15452         this.touchViewHeaderEl.hide();
15453
15454         if(this.modalTitle.length){
15455             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15456             this.touchViewHeaderEl.show();
15457         }
15458
15459         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15460         this.touchViewEl.show();
15461
15462         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15463         
15464         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15465         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15466
15467         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15468
15469         if(this.modalTitle.length){
15470             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15471         }
15472         
15473         this.touchViewBodyEl.setHeight(bodyHeight);
15474
15475         if(this.animate){
15476             var _this = this;
15477             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15478         }else{
15479             this.touchViewEl.addClass('in');
15480         }
15481         
15482         if(this._touchViewMask){
15483             Roo.get(document.body).addClass("x-body-masked");
15484             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15485             this._touchViewMask.setStyle('z-index', 10000);
15486             this._touchViewMask.addClass('show');
15487         }
15488         
15489         this.doTouchViewQuery();
15490         
15491     },
15492     
15493     hideTouchView : function()
15494     {
15495         this.touchViewEl.removeClass('in');
15496
15497         if(this.animate){
15498             var _this = this;
15499             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15500         }else{
15501             this.touchViewEl.setStyle('display', 'none');
15502         }
15503         
15504         if(this._touchViewMask){
15505             this._touchViewMask.removeClass('show');
15506             Roo.get(document.body).removeClass("x-body-masked");
15507         }
15508     },
15509     
15510     setTouchViewValue : function()
15511     {
15512         if(this.multiple){
15513             this.clearItem();
15514         
15515             var _this = this;
15516
15517             Roo.each(this.tickItems, function(o){
15518                 this.addItem(o);
15519             }, this);
15520         }
15521         
15522         this.hideTouchView();
15523     },
15524     
15525     doTouchViewQuery : function()
15526     {
15527         var qe = {
15528             query: '',
15529             forceAll: true,
15530             combo: this,
15531             cancel:false
15532         };
15533         
15534         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15535             return false;
15536         }
15537         
15538         if(!this.alwaysQuery || this.mode == 'local'){
15539             this.onTouchViewLoad();
15540             return;
15541         }
15542         
15543         this.store.load();
15544     },
15545     
15546     onTouchViewBeforeLoad : function(combo,opts)
15547     {
15548         return;
15549     },
15550
15551     // private
15552     onTouchViewLoad : function()
15553     {
15554         if(this.store.getCount() < 1){
15555             this.onTouchViewEmptyResults();
15556             return;
15557         }
15558         
15559         this.clearTouchView();
15560         
15561         var rawValue = this.getRawValue();
15562         
15563         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15564         
15565         this.tickItems = [];
15566         
15567         this.store.data.each(function(d, rowIndex){
15568             var row = this.touchViewListGroup.createChild(template);
15569             
15570             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15571                 row.addClass(d.data.cls);
15572             }
15573             
15574             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15575                 var cfg = {
15576                     data : d.data,
15577                     html : d.data[this.displayField]
15578                 };
15579                 
15580                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15581                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15582                 }
15583             }
15584             row.removeClass('selected');
15585             if(!this.multiple && this.valueField &&
15586                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15587             {
15588                 // radio buttons..
15589                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15590                 row.addClass('selected');
15591             }
15592             
15593             if(this.multiple && this.valueField &&
15594                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15595             {
15596                 
15597                 // checkboxes...
15598                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15599                 this.tickItems.push(d.data);
15600             }
15601             
15602             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15603             
15604         }, this);
15605         
15606         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15607         
15608         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15609
15610         if(this.modalTitle.length){
15611             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15612         }
15613
15614         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15615         
15616         if(this.mobile_restrict_height && listHeight < bodyHeight){
15617             this.touchViewBodyEl.setHeight(listHeight);
15618         }
15619         
15620         var _this = this;
15621         
15622         if(firstChecked && listHeight > bodyHeight){
15623             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15624         }
15625         
15626     },
15627     
15628     onTouchViewLoadException : function()
15629     {
15630         this.hideTouchView();
15631     },
15632     
15633     onTouchViewEmptyResults : function()
15634     {
15635         this.clearTouchView();
15636         
15637         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15638         
15639         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15640         
15641     },
15642     
15643     clearTouchView : function()
15644     {
15645         this.touchViewListGroup.dom.innerHTML = '';
15646     },
15647     
15648     onTouchViewClick : function(e, el, o)
15649     {
15650         e.preventDefault();
15651         
15652         var row = o.row;
15653         var rowIndex = o.rowIndex;
15654         
15655         var r = this.store.getAt(rowIndex);
15656         
15657         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15658             
15659             if(!this.multiple){
15660                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15661                     c.dom.removeAttribute('checked');
15662                 }, this);
15663
15664                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15665
15666                 this.setFromData(r.data);
15667
15668                 var close = this.closeTriggerEl();
15669
15670                 if(close){
15671                     close.show();
15672                 }
15673
15674                 this.hideTouchView();
15675
15676                 this.fireEvent('select', this, r, rowIndex);
15677
15678                 return;
15679             }
15680
15681             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15682                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15683                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15684                 return;
15685             }
15686
15687             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15688             this.addItem(r.data);
15689             this.tickItems.push(r.data);
15690         }
15691     },
15692     
15693     getAutoCreateNativeIOS : function()
15694     {
15695         var cfg = {
15696             cls: 'form-group' //input-group,
15697         };
15698         
15699         var combobox =  {
15700             tag: 'select',
15701             cls : 'roo-ios-select'
15702         };
15703         
15704         if (this.name) {
15705             combobox.name = this.name;
15706         }
15707         
15708         if (this.disabled) {
15709             combobox.disabled = true;
15710         }
15711         
15712         var settings = this;
15713         
15714         ['xs','sm','md','lg'].map(function(size){
15715             if (settings[size]) {
15716                 cfg.cls += ' col-' + size + '-' + settings[size];
15717             }
15718         });
15719         
15720         cfg.cn = combobox;
15721         
15722         return cfg;
15723         
15724     },
15725     
15726     initIOSView : function()
15727     {
15728         this.store.on('load', this.onIOSViewLoad, this);
15729         
15730         return;
15731     },
15732     
15733     onIOSViewLoad : function()
15734     {
15735         if(this.store.getCount() < 1){
15736             return;
15737         }
15738         
15739         this.clearIOSView();
15740         
15741         if(this.allowBlank) {
15742             
15743             var default_text = '-- SELECT --';
15744             
15745             if(this.placeholder.length){
15746                 default_text = this.placeholder;
15747             }
15748             
15749             if(this.emptyTitle.length){
15750                 default_text += ' - ' + this.emptyTitle + ' -';
15751             }
15752             
15753             var opt = this.inputEl().createChild({
15754                 tag: 'option',
15755                 value : 0,
15756                 html : default_text
15757             });
15758             
15759             var o = {};
15760             o[this.valueField] = 0;
15761             o[this.displayField] = default_text;
15762             
15763             this.ios_options.push({
15764                 data : o,
15765                 el : opt
15766             });
15767             
15768         }
15769         
15770         this.store.data.each(function(d, rowIndex){
15771             
15772             var html = '';
15773             
15774             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15775                 html = d.data[this.displayField];
15776             }
15777             
15778             var value = '';
15779             
15780             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15781                 value = d.data[this.valueField];
15782             }
15783             
15784             var option = {
15785                 tag: 'option',
15786                 value : value,
15787                 html : html
15788             };
15789             
15790             if(this.value == d.data[this.valueField]){
15791                 option['selected'] = true;
15792             }
15793             
15794             var opt = this.inputEl().createChild(option);
15795             
15796             this.ios_options.push({
15797                 data : d.data,
15798                 el : opt
15799             });
15800             
15801         }, this);
15802         
15803         this.inputEl().on('change', function(){
15804            this.fireEvent('select', this);
15805         }, this);
15806         
15807     },
15808     
15809     clearIOSView: function()
15810     {
15811         this.inputEl().dom.innerHTML = '';
15812         
15813         this.ios_options = [];
15814     },
15815     
15816     setIOSValue: function(v)
15817     {
15818         this.value = v;
15819         
15820         if(!this.ios_options){
15821             return;
15822         }
15823         
15824         Roo.each(this.ios_options, function(opts){
15825            
15826            opts.el.dom.removeAttribute('selected');
15827            
15828            if(opts.data[this.valueField] != v){
15829                return;
15830            }
15831            
15832            opts.el.dom.setAttribute('selected', true);
15833            
15834         }, this);
15835     }
15836
15837     /** 
15838     * @cfg {Boolean} grow 
15839     * @hide 
15840     */
15841     /** 
15842     * @cfg {Number} growMin 
15843     * @hide 
15844     */
15845     /** 
15846     * @cfg {Number} growMax 
15847     * @hide 
15848     */
15849     /**
15850      * @hide
15851      * @method autoSize
15852      */
15853 });
15854
15855 Roo.apply(Roo.bootstrap.ComboBox,  {
15856     
15857     header : {
15858         tag: 'div',
15859         cls: 'modal-header',
15860         cn: [
15861             {
15862                 tag: 'h4',
15863                 cls: 'modal-title'
15864             }
15865         ]
15866     },
15867     
15868     body : {
15869         tag: 'div',
15870         cls: 'modal-body',
15871         cn: [
15872             {
15873                 tag: 'ul',
15874                 cls: 'list-group'
15875             }
15876         ]
15877     },
15878     
15879     listItemRadio : {
15880         tag: 'li',
15881         cls: 'list-group-item',
15882         cn: [
15883             {
15884                 tag: 'span',
15885                 cls: 'roo-combobox-list-group-item-value'
15886             },
15887             {
15888                 tag: 'div',
15889                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15890                 cn: [
15891                     {
15892                         tag: 'input',
15893                         type: 'radio'
15894                     },
15895                     {
15896                         tag: 'label'
15897                     }
15898                 ]
15899             }
15900         ]
15901     },
15902     
15903     listItemCheckbox : {
15904         tag: 'li',
15905         cls: 'list-group-item',
15906         cn: [
15907             {
15908                 tag: 'span',
15909                 cls: 'roo-combobox-list-group-item-value'
15910             },
15911             {
15912                 tag: 'div',
15913                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15914                 cn: [
15915                     {
15916                         tag: 'input',
15917                         type: 'checkbox'
15918                     },
15919                     {
15920                         tag: 'label'
15921                     }
15922                 ]
15923             }
15924         ]
15925     },
15926     
15927     emptyResult : {
15928         tag: 'div',
15929         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15930     },
15931     
15932     footer : {
15933         tag: 'div',
15934         cls: 'modal-footer',
15935         cn: [
15936             {
15937                 tag: 'div',
15938                 cls: 'row',
15939                 cn: [
15940                     {
15941                         tag: 'div',
15942                         cls: 'col-xs-6 text-left',
15943                         cn: {
15944                             tag: 'button',
15945                             cls: 'btn btn-danger roo-touch-view-cancel',
15946                             html: 'Cancel'
15947                         }
15948                     },
15949                     {
15950                         tag: 'div',
15951                         cls: 'col-xs-6 text-right',
15952                         cn: {
15953                             tag: 'button',
15954                             cls: 'btn btn-success roo-touch-view-ok',
15955                             html: 'OK'
15956                         }
15957                     }
15958                 ]
15959             }
15960         ]
15961         
15962     }
15963 });
15964
15965 Roo.apply(Roo.bootstrap.ComboBox,  {
15966     
15967     touchViewTemplate : {
15968         tag: 'div',
15969         cls: 'modal fade roo-combobox-touch-view',
15970         cn: [
15971             {
15972                 tag: 'div',
15973                 cls: 'modal-dialog',
15974                 style : 'position:fixed', // we have to fix position....
15975                 cn: [
15976                     {
15977                         tag: 'div',
15978                         cls: 'modal-content',
15979                         cn: [
15980                             Roo.bootstrap.ComboBox.header,
15981                             Roo.bootstrap.ComboBox.body,
15982                             Roo.bootstrap.ComboBox.footer
15983                         ]
15984                     }
15985                 ]
15986             }
15987         ]
15988     }
15989 });/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999
16000 /**
16001  * @class Roo.View
16002  * @extends Roo.util.Observable
16003  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16004  * This class also supports single and multi selection modes. <br>
16005  * Create a data model bound view:
16006  <pre><code>
16007  var store = new Roo.data.Store(...);
16008
16009  var view = new Roo.View({
16010     el : "my-element",
16011     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16012  
16013     singleSelect: true,
16014     selectedClass: "ydataview-selected",
16015     store: store
16016  });
16017
16018  // listen for node click?
16019  view.on("click", function(vw, index, node, e){
16020  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16021  });
16022
16023  // load XML data
16024  dataModel.load("foobar.xml");
16025  </code></pre>
16026  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16027  * <br><br>
16028  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16029  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16030  * 
16031  * Note: old style constructor is still suported (container, template, config)
16032  * 
16033  * @constructor
16034  * Create a new View
16035  * @param {Object} config The config object
16036  * 
16037  */
16038 Roo.View = function(config, depreciated_tpl, depreciated_config){
16039     
16040     this.parent = false;
16041     
16042     if (typeof(depreciated_tpl) == 'undefined') {
16043         // new way.. - universal constructor.
16044         Roo.apply(this, config);
16045         this.el  = Roo.get(this.el);
16046     } else {
16047         // old format..
16048         this.el  = Roo.get(config);
16049         this.tpl = depreciated_tpl;
16050         Roo.apply(this, depreciated_config);
16051     }
16052     this.wrapEl  = this.el.wrap().wrap();
16053     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16054     
16055     
16056     if(typeof(this.tpl) == "string"){
16057         this.tpl = new Roo.Template(this.tpl);
16058     } else {
16059         // support xtype ctors..
16060         this.tpl = new Roo.factory(this.tpl, Roo);
16061     }
16062     
16063     
16064     this.tpl.compile();
16065     
16066     /** @private */
16067     this.addEvents({
16068         /**
16069          * @event beforeclick
16070          * Fires before a click is processed. Returns false to cancel the default action.
16071          * @param {Roo.View} this
16072          * @param {Number} index The index of the target node
16073          * @param {HTMLElement} node The target node
16074          * @param {Roo.EventObject} e The raw event object
16075          */
16076             "beforeclick" : true,
16077         /**
16078          * @event click
16079          * Fires when a template node is clicked.
16080          * @param {Roo.View} this
16081          * @param {Number} index The index of the target node
16082          * @param {HTMLElement} node The target node
16083          * @param {Roo.EventObject} e The raw event object
16084          */
16085             "click" : true,
16086         /**
16087          * @event dblclick
16088          * Fires when a template node is double clicked.
16089          * @param {Roo.View} this
16090          * @param {Number} index The index of the target node
16091          * @param {HTMLElement} node The target node
16092          * @param {Roo.EventObject} e The raw event object
16093          */
16094             "dblclick" : true,
16095         /**
16096          * @event contextmenu
16097          * Fires when a template node is right clicked.
16098          * @param {Roo.View} this
16099          * @param {Number} index The index of the target node
16100          * @param {HTMLElement} node The target node
16101          * @param {Roo.EventObject} e The raw event object
16102          */
16103             "contextmenu" : true,
16104         /**
16105          * @event selectionchange
16106          * Fires when the selected nodes change.
16107          * @param {Roo.View} this
16108          * @param {Array} selections Array of the selected nodes
16109          */
16110             "selectionchange" : true,
16111     
16112         /**
16113          * @event beforeselect
16114          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16115          * @param {Roo.View} this
16116          * @param {HTMLElement} node The node to be selected
16117          * @param {Array} selections Array of currently selected nodes
16118          */
16119             "beforeselect" : true,
16120         /**
16121          * @event preparedata
16122          * Fires on every row to render, to allow you to change the data.
16123          * @param {Roo.View} this
16124          * @param {Object} data to be rendered (change this)
16125          */
16126           "preparedata" : true
16127           
16128           
16129         });
16130
16131
16132
16133     this.el.on({
16134         "click": this.onClick,
16135         "dblclick": this.onDblClick,
16136         "contextmenu": this.onContextMenu,
16137         scope:this
16138     });
16139
16140     this.selections = [];
16141     this.nodes = [];
16142     this.cmp = new Roo.CompositeElementLite([]);
16143     if(this.store){
16144         this.store = Roo.factory(this.store, Roo.data);
16145         this.setStore(this.store, true);
16146     }
16147     
16148     if ( this.footer && this.footer.xtype) {
16149            
16150          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16151         
16152         this.footer.dataSource = this.store;
16153         this.footer.container = fctr;
16154         this.footer = Roo.factory(this.footer, Roo);
16155         fctr.insertFirst(this.el);
16156         
16157         // this is a bit insane - as the paging toolbar seems to detach the el..
16158 //        dom.parentNode.parentNode.parentNode
16159          // they get detached?
16160     }
16161     
16162     
16163     Roo.View.superclass.constructor.call(this);
16164     
16165     
16166 };
16167
16168 Roo.extend(Roo.View, Roo.util.Observable, {
16169     
16170      /**
16171      * @cfg {Roo.data.Store} store Data store to load data from.
16172      */
16173     store : false,
16174     
16175     /**
16176      * @cfg {String|Roo.Element} el The container element.
16177      */
16178     el : '',
16179     
16180     /**
16181      * @cfg {String|Roo.Template} tpl The template used by this View 
16182      */
16183     tpl : false,
16184     /**
16185      * @cfg {String} dataName the named area of the template to use as the data area
16186      *                          Works with domtemplates roo-name="name"
16187      */
16188     dataName: false,
16189     /**
16190      * @cfg {String} selectedClass The css class to add to selected nodes
16191      */
16192     selectedClass : "x-view-selected",
16193      /**
16194      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16195      */
16196     emptyText : "",
16197     
16198     /**
16199      * @cfg {String} text to display on mask (default Loading)
16200      */
16201     mask : false,
16202     /**
16203      * @cfg {Boolean} multiSelect Allow multiple selection
16204      */
16205     multiSelect : false,
16206     /**
16207      * @cfg {Boolean} singleSelect Allow single selection
16208      */
16209     singleSelect:  false,
16210     
16211     /**
16212      * @cfg {Boolean} toggleSelect - selecting 
16213      */
16214     toggleSelect : false,
16215     
16216     /**
16217      * @cfg {Boolean} tickable - selecting 
16218      */
16219     tickable : false,
16220     
16221     /**
16222      * Returns the element this view is bound to.
16223      * @return {Roo.Element}
16224      */
16225     getEl : function(){
16226         return this.wrapEl;
16227     },
16228     
16229     
16230
16231     /**
16232      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16233      */
16234     refresh : function(){
16235         //Roo.log('refresh');
16236         var t = this.tpl;
16237         
16238         // if we are using something like 'domtemplate', then
16239         // the what gets used is:
16240         // t.applySubtemplate(NAME, data, wrapping data..)
16241         // the outer template then get' applied with
16242         //     the store 'extra data'
16243         // and the body get's added to the
16244         //      roo-name="data" node?
16245         //      <span class='roo-tpl-{name}'></span> ?????
16246         
16247         
16248         
16249         this.clearSelections();
16250         this.el.update("");
16251         var html = [];
16252         var records = this.store.getRange();
16253         if(records.length < 1) {
16254             
16255             // is this valid??  = should it render a template??
16256             
16257             this.el.update(this.emptyText);
16258             return;
16259         }
16260         var el = this.el;
16261         if (this.dataName) {
16262             this.el.update(t.apply(this.store.meta)); //????
16263             el = this.el.child('.roo-tpl-' + this.dataName);
16264         }
16265         
16266         for(var i = 0, len = records.length; i < len; i++){
16267             var data = this.prepareData(records[i].data, i, records[i]);
16268             this.fireEvent("preparedata", this, data, i, records[i]);
16269             
16270             var d = Roo.apply({}, data);
16271             
16272             if(this.tickable){
16273                 Roo.apply(d, {'roo-id' : Roo.id()});
16274                 
16275                 var _this = this;
16276             
16277                 Roo.each(this.parent.item, function(item){
16278                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16279                         return;
16280                     }
16281                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16282                 });
16283             }
16284             
16285             html[html.length] = Roo.util.Format.trim(
16286                 this.dataName ?
16287                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16288                     t.apply(d)
16289             );
16290         }
16291         
16292         
16293         
16294         el.update(html.join(""));
16295         this.nodes = el.dom.childNodes;
16296         this.updateIndexes(0);
16297     },
16298     
16299
16300     /**
16301      * Function to override to reformat the data that is sent to
16302      * the template for each node.
16303      * DEPRICATED - use the preparedata event handler.
16304      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16305      * a JSON object for an UpdateManager bound view).
16306      */
16307     prepareData : function(data, index, record)
16308     {
16309         this.fireEvent("preparedata", this, data, index, record);
16310         return data;
16311     },
16312
16313     onUpdate : function(ds, record){
16314         // Roo.log('on update');   
16315         this.clearSelections();
16316         var index = this.store.indexOf(record);
16317         var n = this.nodes[index];
16318         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16319         n.parentNode.removeChild(n);
16320         this.updateIndexes(index, index);
16321     },
16322
16323     
16324     
16325 // --------- FIXME     
16326     onAdd : function(ds, records, index)
16327     {
16328         //Roo.log(['on Add', ds, records, index] );        
16329         this.clearSelections();
16330         if(this.nodes.length == 0){
16331             this.refresh();
16332             return;
16333         }
16334         var n = this.nodes[index];
16335         for(var i = 0, len = records.length; i < len; i++){
16336             var d = this.prepareData(records[i].data, i, records[i]);
16337             if(n){
16338                 this.tpl.insertBefore(n, d);
16339             }else{
16340                 
16341                 this.tpl.append(this.el, d);
16342             }
16343         }
16344         this.updateIndexes(index);
16345     },
16346
16347     onRemove : function(ds, record, index){
16348        // Roo.log('onRemove');
16349         this.clearSelections();
16350         var el = this.dataName  ?
16351             this.el.child('.roo-tpl-' + this.dataName) :
16352             this.el; 
16353         
16354         el.dom.removeChild(this.nodes[index]);
16355         this.updateIndexes(index);
16356     },
16357
16358     /**
16359      * Refresh an individual node.
16360      * @param {Number} index
16361      */
16362     refreshNode : function(index){
16363         this.onUpdate(this.store, this.store.getAt(index));
16364     },
16365
16366     updateIndexes : function(startIndex, endIndex){
16367         var ns = this.nodes;
16368         startIndex = startIndex || 0;
16369         endIndex = endIndex || ns.length - 1;
16370         for(var i = startIndex; i <= endIndex; i++){
16371             ns[i].nodeIndex = i;
16372         }
16373     },
16374
16375     /**
16376      * Changes the data store this view uses and refresh the view.
16377      * @param {Store} store
16378      */
16379     setStore : function(store, initial){
16380         if(!initial && this.store){
16381             this.store.un("datachanged", this.refresh);
16382             this.store.un("add", this.onAdd);
16383             this.store.un("remove", this.onRemove);
16384             this.store.un("update", this.onUpdate);
16385             this.store.un("clear", this.refresh);
16386             this.store.un("beforeload", this.onBeforeLoad);
16387             this.store.un("load", this.onLoad);
16388             this.store.un("loadexception", this.onLoad);
16389         }
16390         if(store){
16391           
16392             store.on("datachanged", this.refresh, this);
16393             store.on("add", this.onAdd, this);
16394             store.on("remove", this.onRemove, this);
16395             store.on("update", this.onUpdate, this);
16396             store.on("clear", this.refresh, this);
16397             store.on("beforeload", this.onBeforeLoad, this);
16398             store.on("load", this.onLoad, this);
16399             store.on("loadexception", this.onLoad, this);
16400         }
16401         
16402         if(store){
16403             this.refresh();
16404         }
16405     },
16406     /**
16407      * onbeforeLoad - masks the loading area.
16408      *
16409      */
16410     onBeforeLoad : function(store,opts)
16411     {
16412          //Roo.log('onBeforeLoad');   
16413         if (!opts.add) {
16414             this.el.update("");
16415         }
16416         this.el.mask(this.mask ? this.mask : "Loading" ); 
16417     },
16418     onLoad : function ()
16419     {
16420         this.el.unmask();
16421     },
16422     
16423
16424     /**
16425      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16426      * @param {HTMLElement} node
16427      * @return {HTMLElement} The template node
16428      */
16429     findItemFromChild : function(node){
16430         var el = this.dataName  ?
16431             this.el.child('.roo-tpl-' + this.dataName,true) :
16432             this.el.dom; 
16433         
16434         if(!node || node.parentNode == el){
16435                     return node;
16436             }
16437             var p = node.parentNode;
16438             while(p && p != el){
16439             if(p.parentNode == el){
16440                 return p;
16441             }
16442             p = p.parentNode;
16443         }
16444             return null;
16445     },
16446
16447     /** @ignore */
16448     onClick : function(e){
16449         var item = this.findItemFromChild(e.getTarget());
16450         if(item){
16451             var index = this.indexOf(item);
16452             if(this.onItemClick(item, index, e) !== false){
16453                 this.fireEvent("click", this, index, item, e);
16454             }
16455         }else{
16456             this.clearSelections();
16457         }
16458     },
16459
16460     /** @ignore */
16461     onContextMenu : function(e){
16462         var item = this.findItemFromChild(e.getTarget());
16463         if(item){
16464             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16465         }
16466     },
16467
16468     /** @ignore */
16469     onDblClick : function(e){
16470         var item = this.findItemFromChild(e.getTarget());
16471         if(item){
16472             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16473         }
16474     },
16475
16476     onItemClick : function(item, index, e)
16477     {
16478         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16479             return false;
16480         }
16481         if (this.toggleSelect) {
16482             var m = this.isSelected(item) ? 'unselect' : 'select';
16483             //Roo.log(m);
16484             var _t = this;
16485             _t[m](item, true, false);
16486             return true;
16487         }
16488         if(this.multiSelect || this.singleSelect){
16489             if(this.multiSelect && e.shiftKey && this.lastSelection){
16490                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16491             }else{
16492                 this.select(item, this.multiSelect && e.ctrlKey);
16493                 this.lastSelection = item;
16494             }
16495             
16496             if(!this.tickable){
16497                 e.preventDefault();
16498             }
16499             
16500         }
16501         return true;
16502     },
16503
16504     /**
16505      * Get the number of selected nodes.
16506      * @return {Number}
16507      */
16508     getSelectionCount : function(){
16509         return this.selections.length;
16510     },
16511
16512     /**
16513      * Get the currently selected nodes.
16514      * @return {Array} An array of HTMLElements
16515      */
16516     getSelectedNodes : function(){
16517         return this.selections;
16518     },
16519
16520     /**
16521      * Get the indexes of the selected nodes.
16522      * @return {Array}
16523      */
16524     getSelectedIndexes : function(){
16525         var indexes = [], s = this.selections;
16526         for(var i = 0, len = s.length; i < len; i++){
16527             indexes.push(s[i].nodeIndex);
16528         }
16529         return indexes;
16530     },
16531
16532     /**
16533      * Clear all selections
16534      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16535      */
16536     clearSelections : function(suppressEvent){
16537         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16538             this.cmp.elements = this.selections;
16539             this.cmp.removeClass(this.selectedClass);
16540             this.selections = [];
16541             if(!suppressEvent){
16542                 this.fireEvent("selectionchange", this, this.selections);
16543             }
16544         }
16545     },
16546
16547     /**
16548      * Returns true if the passed node is selected
16549      * @param {HTMLElement/Number} node The node or node index
16550      * @return {Boolean}
16551      */
16552     isSelected : function(node){
16553         var s = this.selections;
16554         if(s.length < 1){
16555             return false;
16556         }
16557         node = this.getNode(node);
16558         return s.indexOf(node) !== -1;
16559     },
16560
16561     /**
16562      * Selects nodes.
16563      * @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
16564      * @param {Boolean} keepExisting (optional) true to keep existing selections
16565      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16566      */
16567     select : function(nodeInfo, keepExisting, suppressEvent){
16568         if(nodeInfo instanceof Array){
16569             if(!keepExisting){
16570                 this.clearSelections(true);
16571             }
16572             for(var i = 0, len = nodeInfo.length; i < len; i++){
16573                 this.select(nodeInfo[i], true, true);
16574             }
16575             return;
16576         } 
16577         var node = this.getNode(nodeInfo);
16578         if(!node || this.isSelected(node)){
16579             return; // already selected.
16580         }
16581         if(!keepExisting){
16582             this.clearSelections(true);
16583         }
16584         
16585         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16586             Roo.fly(node).addClass(this.selectedClass);
16587             this.selections.push(node);
16588             if(!suppressEvent){
16589                 this.fireEvent("selectionchange", this, this.selections);
16590             }
16591         }
16592         
16593         
16594     },
16595       /**
16596      * Unselects nodes.
16597      * @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
16598      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16599      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16600      */
16601     unselect : function(nodeInfo, keepExisting, suppressEvent)
16602     {
16603         if(nodeInfo instanceof Array){
16604             Roo.each(this.selections, function(s) {
16605                 this.unselect(s, nodeInfo);
16606             }, this);
16607             return;
16608         }
16609         var node = this.getNode(nodeInfo);
16610         if(!node || !this.isSelected(node)){
16611             //Roo.log("not selected");
16612             return; // not selected.
16613         }
16614         // fireevent???
16615         var ns = [];
16616         Roo.each(this.selections, function(s) {
16617             if (s == node ) {
16618                 Roo.fly(node).removeClass(this.selectedClass);
16619
16620                 return;
16621             }
16622             ns.push(s);
16623         },this);
16624         
16625         this.selections= ns;
16626         this.fireEvent("selectionchange", this, this.selections);
16627     },
16628
16629     /**
16630      * Gets a template node.
16631      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16632      * @return {HTMLElement} The node or null if it wasn't found
16633      */
16634     getNode : function(nodeInfo){
16635         if(typeof nodeInfo == "string"){
16636             return document.getElementById(nodeInfo);
16637         }else if(typeof nodeInfo == "number"){
16638             return this.nodes[nodeInfo];
16639         }
16640         return nodeInfo;
16641     },
16642
16643     /**
16644      * Gets a range template nodes.
16645      * @param {Number} startIndex
16646      * @param {Number} endIndex
16647      * @return {Array} An array of nodes
16648      */
16649     getNodes : function(start, end){
16650         var ns = this.nodes;
16651         start = start || 0;
16652         end = typeof end == "undefined" ? ns.length - 1 : end;
16653         var nodes = [];
16654         if(start <= end){
16655             for(var i = start; i <= end; i++){
16656                 nodes.push(ns[i]);
16657             }
16658         } else{
16659             for(var i = start; i >= end; i--){
16660                 nodes.push(ns[i]);
16661             }
16662         }
16663         return nodes;
16664     },
16665
16666     /**
16667      * Finds the index of the passed node
16668      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16669      * @return {Number} The index of the node or -1
16670      */
16671     indexOf : function(node){
16672         node = this.getNode(node);
16673         if(typeof node.nodeIndex == "number"){
16674             return node.nodeIndex;
16675         }
16676         var ns = this.nodes;
16677         for(var i = 0, len = ns.length; i < len; i++){
16678             if(ns[i] == node){
16679                 return i;
16680             }
16681         }
16682         return -1;
16683     }
16684 });
16685 /*
16686  * - LGPL
16687  *
16688  * based on jquery fullcalendar
16689  * 
16690  */
16691
16692 Roo.bootstrap = Roo.bootstrap || {};
16693 /**
16694  * @class Roo.bootstrap.Calendar
16695  * @extends Roo.bootstrap.Component
16696  * Bootstrap Calendar class
16697  * @cfg {Boolean} loadMask (true|false) default false
16698  * @cfg {Object} header generate the user specific header of the calendar, default false
16699
16700  * @constructor
16701  * Create a new Container
16702  * @param {Object} config The config object
16703  */
16704
16705
16706
16707 Roo.bootstrap.Calendar = function(config){
16708     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16709      this.addEvents({
16710         /**
16711              * @event select
16712              * Fires when a date is selected
16713              * @param {DatePicker} this
16714              * @param {Date} date The selected date
16715              */
16716         'select': true,
16717         /**
16718              * @event monthchange
16719              * Fires when the displayed month changes 
16720              * @param {DatePicker} this
16721              * @param {Date} date The selected month
16722              */
16723         'monthchange': true,
16724         /**
16725              * @event evententer
16726              * Fires when mouse over an event
16727              * @param {Calendar} this
16728              * @param {event} Event
16729              */
16730         'evententer': true,
16731         /**
16732              * @event eventleave
16733              * Fires when the mouse leaves an
16734              * @param {Calendar} this
16735              * @param {event}
16736              */
16737         'eventleave': true,
16738         /**
16739              * @event eventclick
16740              * Fires when the mouse click an
16741              * @param {Calendar} this
16742              * @param {event}
16743              */
16744         'eventclick': true
16745         
16746     });
16747
16748 };
16749
16750 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16751     
16752      /**
16753      * @cfg {Number} startDay
16754      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16755      */
16756     startDay : 0,
16757     
16758     loadMask : false,
16759     
16760     header : false,
16761       
16762     getAutoCreate : function(){
16763         
16764         
16765         var fc_button = function(name, corner, style, content ) {
16766             return Roo.apply({},{
16767                 tag : 'span',
16768                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16769                          (corner.length ?
16770                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16771                             ''
16772                         ),
16773                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16774                 unselectable: 'on'
16775             });
16776         };
16777         
16778         var header = {};
16779         
16780         if(!this.header){
16781             header = {
16782                 tag : 'table',
16783                 cls : 'fc-header',
16784                 style : 'width:100%',
16785                 cn : [
16786                     {
16787                         tag: 'tr',
16788                         cn : [
16789                             {
16790                                 tag : 'td',
16791                                 cls : 'fc-header-left',
16792                                 cn : [
16793                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16794                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16795                                     { tag: 'span', cls: 'fc-header-space' },
16796                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16797
16798
16799                                 ]
16800                             },
16801
16802                             {
16803                                 tag : 'td',
16804                                 cls : 'fc-header-center',
16805                                 cn : [
16806                                     {
16807                                         tag: 'span',
16808                                         cls: 'fc-header-title',
16809                                         cn : {
16810                                             tag: 'H2',
16811                                             html : 'month / year'
16812                                         }
16813                                     }
16814
16815                                 ]
16816                             },
16817                             {
16818                                 tag : 'td',
16819                                 cls : 'fc-header-right',
16820                                 cn : [
16821                               /*      fc_button('month', 'left', '', 'month' ),
16822                                     fc_button('week', '', '', 'week' ),
16823                                     fc_button('day', 'right', '', 'day' )
16824                                 */    
16825
16826                                 ]
16827                             }
16828
16829                         ]
16830                     }
16831                 ]
16832             };
16833         }
16834         
16835         header = this.header;
16836         
16837        
16838         var cal_heads = function() {
16839             var ret = [];
16840             // fixme - handle this.
16841             
16842             for (var i =0; i < Date.dayNames.length; i++) {
16843                 var d = Date.dayNames[i];
16844                 ret.push({
16845                     tag: 'th',
16846                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16847                     html : d.substring(0,3)
16848                 });
16849                 
16850             }
16851             ret[0].cls += ' fc-first';
16852             ret[6].cls += ' fc-last';
16853             return ret;
16854         };
16855         var cal_cell = function(n) {
16856             return  {
16857                 tag: 'td',
16858                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16859                 cn : [
16860                     {
16861                         cn : [
16862                             {
16863                                 cls: 'fc-day-number',
16864                                 html: 'D'
16865                             },
16866                             {
16867                                 cls: 'fc-day-content',
16868                              
16869                                 cn : [
16870                                      {
16871                                         style: 'position: relative;' // height: 17px;
16872                                     }
16873                                 ]
16874                             }
16875                             
16876                             
16877                         ]
16878                     }
16879                 ]
16880                 
16881             }
16882         };
16883         var cal_rows = function() {
16884             
16885             var ret = [];
16886             for (var r = 0; r < 6; r++) {
16887                 var row= {
16888                     tag : 'tr',
16889                     cls : 'fc-week',
16890                     cn : []
16891                 };
16892                 
16893                 for (var i =0; i < Date.dayNames.length; i++) {
16894                     var d = Date.dayNames[i];
16895                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16896
16897                 }
16898                 row.cn[0].cls+=' fc-first';
16899                 row.cn[0].cn[0].style = 'min-height:90px';
16900                 row.cn[6].cls+=' fc-last';
16901                 ret.push(row);
16902                 
16903             }
16904             ret[0].cls += ' fc-first';
16905             ret[4].cls += ' fc-prev-last';
16906             ret[5].cls += ' fc-last';
16907             return ret;
16908             
16909         };
16910         
16911         var cal_table = {
16912             tag: 'table',
16913             cls: 'fc-border-separate',
16914             style : 'width:100%',
16915             cellspacing  : 0,
16916             cn : [
16917                 { 
16918                     tag: 'thead',
16919                     cn : [
16920                         { 
16921                             tag: 'tr',
16922                             cls : 'fc-first fc-last',
16923                             cn : cal_heads()
16924                         }
16925                     ]
16926                 },
16927                 { 
16928                     tag: 'tbody',
16929                     cn : cal_rows()
16930                 }
16931                   
16932             ]
16933         };
16934          
16935          var cfg = {
16936             cls : 'fc fc-ltr',
16937             cn : [
16938                 header,
16939                 {
16940                     cls : 'fc-content',
16941                     style : "position: relative;",
16942                     cn : [
16943                         {
16944                             cls : 'fc-view fc-view-month fc-grid',
16945                             style : 'position: relative',
16946                             unselectable : 'on',
16947                             cn : [
16948                                 {
16949                                     cls : 'fc-event-container',
16950                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16951                                 },
16952                                 cal_table
16953                             ]
16954                         }
16955                     ]
16956     
16957                 }
16958            ] 
16959             
16960         };
16961         
16962          
16963         
16964         return cfg;
16965     },
16966     
16967     
16968     initEvents : function()
16969     {
16970         if(!this.store){
16971             throw "can not find store for calendar";
16972         }
16973         
16974         var mark = {
16975             tag: "div",
16976             cls:"x-dlg-mask",
16977             style: "text-align:center",
16978             cn: [
16979                 {
16980                     tag: "div",
16981                     style: "background-color:white;width:50%;margin:250 auto",
16982                     cn: [
16983                         {
16984                             tag: "img",
16985                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16986                         },
16987                         {
16988                             tag: "span",
16989                             html: "Loading"
16990                         }
16991                         
16992                     ]
16993                 }
16994             ]
16995         };
16996         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16997         
16998         var size = this.el.select('.fc-content', true).first().getSize();
16999         this.maskEl.setSize(size.width, size.height);
17000         this.maskEl.enableDisplayMode("block");
17001         if(!this.loadMask){
17002             this.maskEl.hide();
17003         }
17004         
17005         this.store = Roo.factory(this.store, Roo.data);
17006         this.store.on('load', this.onLoad, this);
17007         this.store.on('beforeload', this.onBeforeLoad, this);
17008         
17009         this.resize();
17010         
17011         this.cells = this.el.select('.fc-day',true);
17012         //Roo.log(this.cells);
17013         this.textNodes = this.el.query('.fc-day-number');
17014         this.cells.addClassOnOver('fc-state-hover');
17015         
17016         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17017         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17018         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17019         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17020         
17021         this.on('monthchange', this.onMonthChange, this);
17022         
17023         this.update(new Date().clearTime());
17024     },
17025     
17026     resize : function() {
17027         var sz  = this.el.getSize();
17028         
17029         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17030         this.el.select('.fc-day-content div',true).setHeight(34);
17031     },
17032     
17033     
17034     // private
17035     showPrevMonth : function(e){
17036         this.update(this.activeDate.add("mo", -1));
17037     },
17038     showToday : function(e){
17039         this.update(new Date().clearTime());
17040     },
17041     // private
17042     showNextMonth : function(e){
17043         this.update(this.activeDate.add("mo", 1));
17044     },
17045
17046     // private
17047     showPrevYear : function(){
17048         this.update(this.activeDate.add("y", -1));
17049     },
17050
17051     // private
17052     showNextYear : function(){
17053         this.update(this.activeDate.add("y", 1));
17054     },
17055
17056     
17057    // private
17058     update : function(date)
17059     {
17060         var vd = this.activeDate;
17061         this.activeDate = date;
17062 //        if(vd && this.el){
17063 //            var t = date.getTime();
17064 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17065 //                Roo.log('using add remove');
17066 //                
17067 //                this.fireEvent('monthchange', this, date);
17068 //                
17069 //                this.cells.removeClass("fc-state-highlight");
17070 //                this.cells.each(function(c){
17071 //                   if(c.dateValue == t){
17072 //                       c.addClass("fc-state-highlight");
17073 //                       setTimeout(function(){
17074 //                            try{c.dom.firstChild.focus();}catch(e){}
17075 //                       }, 50);
17076 //                       return false;
17077 //                   }
17078 //                   return true;
17079 //                });
17080 //                return;
17081 //            }
17082 //        }
17083         
17084         var days = date.getDaysInMonth();
17085         
17086         var firstOfMonth = date.getFirstDateOfMonth();
17087         var startingPos = firstOfMonth.getDay()-this.startDay;
17088         
17089         if(startingPos < this.startDay){
17090             startingPos += 7;
17091         }
17092         
17093         var pm = date.add(Date.MONTH, -1);
17094         var prevStart = pm.getDaysInMonth()-startingPos;
17095 //        
17096         this.cells = this.el.select('.fc-day',true);
17097         this.textNodes = this.el.query('.fc-day-number');
17098         this.cells.addClassOnOver('fc-state-hover');
17099         
17100         var cells = this.cells.elements;
17101         var textEls = this.textNodes;
17102         
17103         Roo.each(cells, function(cell){
17104             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17105         });
17106         
17107         days += startingPos;
17108
17109         // convert everything to numbers so it's fast
17110         var day = 86400000;
17111         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17112         //Roo.log(d);
17113         //Roo.log(pm);
17114         //Roo.log(prevStart);
17115         
17116         var today = new Date().clearTime().getTime();
17117         var sel = date.clearTime().getTime();
17118         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17119         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17120         var ddMatch = this.disabledDatesRE;
17121         var ddText = this.disabledDatesText;
17122         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17123         var ddaysText = this.disabledDaysText;
17124         var format = this.format;
17125         
17126         var setCellClass = function(cal, cell){
17127             cell.row = 0;
17128             cell.events = [];
17129             cell.more = [];
17130             //Roo.log('set Cell Class');
17131             cell.title = "";
17132             var t = d.getTime();
17133             
17134             //Roo.log(d);
17135             
17136             cell.dateValue = t;
17137             if(t == today){
17138                 cell.className += " fc-today";
17139                 cell.className += " fc-state-highlight";
17140                 cell.title = cal.todayText;
17141             }
17142             if(t == sel){
17143                 // disable highlight in other month..
17144                 //cell.className += " fc-state-highlight";
17145                 
17146             }
17147             // disabling
17148             if(t < min) {
17149                 cell.className = " fc-state-disabled";
17150                 cell.title = cal.minText;
17151                 return;
17152             }
17153             if(t > max) {
17154                 cell.className = " fc-state-disabled";
17155                 cell.title = cal.maxText;
17156                 return;
17157             }
17158             if(ddays){
17159                 if(ddays.indexOf(d.getDay()) != -1){
17160                     cell.title = ddaysText;
17161                     cell.className = " fc-state-disabled";
17162                 }
17163             }
17164             if(ddMatch && format){
17165                 var fvalue = d.dateFormat(format);
17166                 if(ddMatch.test(fvalue)){
17167                     cell.title = ddText.replace("%0", fvalue);
17168                     cell.className = " fc-state-disabled";
17169                 }
17170             }
17171             
17172             if (!cell.initialClassName) {
17173                 cell.initialClassName = cell.dom.className;
17174             }
17175             
17176             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17177         };
17178
17179         var i = 0;
17180         
17181         for(; i < startingPos; i++) {
17182             textEls[i].innerHTML = (++prevStart);
17183             d.setDate(d.getDate()+1);
17184             
17185             cells[i].className = "fc-past fc-other-month";
17186             setCellClass(this, cells[i]);
17187         }
17188         
17189         var intDay = 0;
17190         
17191         for(; i < days; i++){
17192             intDay = i - startingPos + 1;
17193             textEls[i].innerHTML = (intDay);
17194             d.setDate(d.getDate()+1);
17195             
17196             cells[i].className = ''; // "x-date-active";
17197             setCellClass(this, cells[i]);
17198         }
17199         var extraDays = 0;
17200         
17201         for(; i < 42; i++) {
17202             textEls[i].innerHTML = (++extraDays);
17203             d.setDate(d.getDate()+1);
17204             
17205             cells[i].className = "fc-future fc-other-month";
17206             setCellClass(this, cells[i]);
17207         }
17208         
17209         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17210         
17211         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17212         
17213         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17214         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17215         
17216         if(totalRows != 6){
17217             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17218             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17219         }
17220         
17221         this.fireEvent('monthchange', this, date);
17222         
17223         
17224         /*
17225         if(!this.internalRender){
17226             var main = this.el.dom.firstChild;
17227             var w = main.offsetWidth;
17228             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17229             Roo.fly(main).setWidth(w);
17230             this.internalRender = true;
17231             // opera does not respect the auto grow header center column
17232             // then, after it gets a width opera refuses to recalculate
17233             // without a second pass
17234             if(Roo.isOpera && !this.secondPass){
17235                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17236                 this.secondPass = true;
17237                 this.update.defer(10, this, [date]);
17238             }
17239         }
17240         */
17241         
17242     },
17243     
17244     findCell : function(dt) {
17245         dt = dt.clearTime().getTime();
17246         var ret = false;
17247         this.cells.each(function(c){
17248             //Roo.log("check " +c.dateValue + '?=' + dt);
17249             if(c.dateValue == dt){
17250                 ret = c;
17251                 return false;
17252             }
17253             return true;
17254         });
17255         
17256         return ret;
17257     },
17258     
17259     findCells : function(ev) {
17260         var s = ev.start.clone().clearTime().getTime();
17261        // Roo.log(s);
17262         var e= ev.end.clone().clearTime().getTime();
17263        // Roo.log(e);
17264         var ret = [];
17265         this.cells.each(function(c){
17266              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17267             
17268             if(c.dateValue > e){
17269                 return ;
17270             }
17271             if(c.dateValue < s){
17272                 return ;
17273             }
17274             ret.push(c);
17275         });
17276         
17277         return ret;    
17278     },
17279     
17280 //    findBestRow: function(cells)
17281 //    {
17282 //        var ret = 0;
17283 //        
17284 //        for (var i =0 ; i < cells.length;i++) {
17285 //            ret  = Math.max(cells[i].rows || 0,ret);
17286 //        }
17287 //        return ret;
17288 //        
17289 //    },
17290     
17291     
17292     addItem : function(ev)
17293     {
17294         // look for vertical location slot in
17295         var cells = this.findCells(ev);
17296         
17297 //        ev.row = this.findBestRow(cells);
17298         
17299         // work out the location.
17300         
17301         var crow = false;
17302         var rows = [];
17303         for(var i =0; i < cells.length; i++) {
17304             
17305             cells[i].row = cells[0].row;
17306             
17307             if(i == 0){
17308                 cells[i].row = cells[i].row + 1;
17309             }
17310             
17311             if (!crow) {
17312                 crow = {
17313                     start : cells[i],
17314                     end :  cells[i]
17315                 };
17316                 continue;
17317             }
17318             if (crow.start.getY() == cells[i].getY()) {
17319                 // on same row.
17320                 crow.end = cells[i];
17321                 continue;
17322             }
17323             // different row.
17324             rows.push(crow);
17325             crow = {
17326                 start: cells[i],
17327                 end : cells[i]
17328             };
17329             
17330         }
17331         
17332         rows.push(crow);
17333         ev.els = [];
17334         ev.rows = rows;
17335         ev.cells = cells;
17336         
17337         cells[0].events.push(ev);
17338         
17339         this.calevents.push(ev);
17340     },
17341     
17342     clearEvents: function() {
17343         
17344         if(!this.calevents){
17345             return;
17346         }
17347         
17348         Roo.each(this.cells.elements, function(c){
17349             c.row = 0;
17350             c.events = [];
17351             c.more = [];
17352         });
17353         
17354         Roo.each(this.calevents, function(e) {
17355             Roo.each(e.els, function(el) {
17356                 el.un('mouseenter' ,this.onEventEnter, this);
17357                 el.un('mouseleave' ,this.onEventLeave, this);
17358                 el.remove();
17359             },this);
17360         },this);
17361         
17362         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17363             e.remove();
17364         });
17365         
17366     },
17367     
17368     renderEvents: function()
17369     {   
17370         var _this = this;
17371         
17372         this.cells.each(function(c) {
17373             
17374             if(c.row < 5){
17375                 return;
17376             }
17377             
17378             var ev = c.events;
17379             
17380             var r = 4;
17381             if(c.row != c.events.length){
17382                 r = 4 - (4 - (c.row - c.events.length));
17383             }
17384             
17385             c.events = ev.slice(0, r);
17386             c.more = ev.slice(r);
17387             
17388             if(c.more.length && c.more.length == 1){
17389                 c.events.push(c.more.pop());
17390             }
17391             
17392             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17393             
17394         });
17395             
17396         this.cells.each(function(c) {
17397             
17398             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17399             
17400             
17401             for (var e = 0; e < c.events.length; e++){
17402                 var ev = c.events[e];
17403                 var rows = ev.rows;
17404                 
17405                 for(var i = 0; i < rows.length; i++) {
17406                 
17407                     // how many rows should it span..
17408
17409                     var  cfg = {
17410                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17411                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17412
17413                         unselectable : "on",
17414                         cn : [
17415                             {
17416                                 cls: 'fc-event-inner',
17417                                 cn : [
17418     //                                {
17419     //                                  tag:'span',
17420     //                                  cls: 'fc-event-time',
17421     //                                  html : cells.length > 1 ? '' : ev.time
17422     //                                },
17423                                     {
17424                                       tag:'span',
17425                                       cls: 'fc-event-title',
17426                                       html : String.format('{0}', ev.title)
17427                                     }
17428
17429
17430                                 ]
17431                             },
17432                             {
17433                                 cls: 'ui-resizable-handle ui-resizable-e',
17434                                 html : '&nbsp;&nbsp;&nbsp'
17435                             }
17436
17437                         ]
17438                     };
17439
17440                     if (i == 0) {
17441                         cfg.cls += ' fc-event-start';
17442                     }
17443                     if ((i+1) == rows.length) {
17444                         cfg.cls += ' fc-event-end';
17445                     }
17446
17447                     var ctr = _this.el.select('.fc-event-container',true).first();
17448                     var cg = ctr.createChild(cfg);
17449
17450                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17451                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17452
17453                     var r = (c.more.length) ? 1 : 0;
17454                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17455                     cg.setWidth(ebox.right - sbox.x -2);
17456
17457                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17458                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17459                     cg.on('click', _this.onEventClick, _this, ev);
17460
17461                     ev.els.push(cg);
17462                     
17463                 }
17464                 
17465             }
17466             
17467             
17468             if(c.more.length){
17469                 var  cfg = {
17470                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17471                     style : 'position: absolute',
17472                     unselectable : "on",
17473                     cn : [
17474                         {
17475                             cls: 'fc-event-inner',
17476                             cn : [
17477                                 {
17478                                   tag:'span',
17479                                   cls: 'fc-event-title',
17480                                   html : 'More'
17481                                 }
17482
17483
17484                             ]
17485                         },
17486                         {
17487                             cls: 'ui-resizable-handle ui-resizable-e',
17488                             html : '&nbsp;&nbsp;&nbsp'
17489                         }
17490
17491                     ]
17492                 };
17493
17494                 var ctr = _this.el.select('.fc-event-container',true).first();
17495                 var cg = ctr.createChild(cfg);
17496
17497                 var sbox = c.select('.fc-day-content',true).first().getBox();
17498                 var ebox = c.select('.fc-day-content',true).first().getBox();
17499                 //Roo.log(cg);
17500                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17501                 cg.setWidth(ebox.right - sbox.x -2);
17502
17503                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17504                 
17505             }
17506             
17507         });
17508         
17509         
17510         
17511     },
17512     
17513     onEventEnter: function (e, el,event,d) {
17514         this.fireEvent('evententer', this, el, event);
17515     },
17516     
17517     onEventLeave: function (e, el,event,d) {
17518         this.fireEvent('eventleave', this, el, event);
17519     },
17520     
17521     onEventClick: function (e, el,event,d) {
17522         this.fireEvent('eventclick', this, el, event);
17523     },
17524     
17525     onMonthChange: function () {
17526         this.store.load();
17527     },
17528     
17529     onMoreEventClick: function(e, el, more)
17530     {
17531         var _this = this;
17532         
17533         this.calpopover.placement = 'right';
17534         this.calpopover.setTitle('More');
17535         
17536         this.calpopover.setContent('');
17537         
17538         var ctr = this.calpopover.el.select('.popover-content', true).first();
17539         
17540         Roo.each(more, function(m){
17541             var cfg = {
17542                 cls : 'fc-event-hori fc-event-draggable',
17543                 html : m.title
17544             };
17545             var cg = ctr.createChild(cfg);
17546             
17547             cg.on('click', _this.onEventClick, _this, m);
17548         });
17549         
17550         this.calpopover.show(el);
17551         
17552         
17553     },
17554     
17555     onLoad: function () 
17556     {   
17557         this.calevents = [];
17558         var cal = this;
17559         
17560         if(this.store.getCount() > 0){
17561             this.store.data.each(function(d){
17562                cal.addItem({
17563                     id : d.data.id,
17564                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17565                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17566                     time : d.data.start_time,
17567                     title : d.data.title,
17568                     description : d.data.description,
17569                     venue : d.data.venue
17570                 });
17571             });
17572         }
17573         
17574         this.renderEvents();
17575         
17576         if(this.calevents.length && this.loadMask){
17577             this.maskEl.hide();
17578         }
17579     },
17580     
17581     onBeforeLoad: function()
17582     {
17583         this.clearEvents();
17584         if(this.loadMask){
17585             this.maskEl.show();
17586         }
17587     }
17588 });
17589
17590  
17591  /*
17592  * - LGPL
17593  *
17594  * element
17595  * 
17596  */
17597
17598 /**
17599  * @class Roo.bootstrap.Popover
17600  * @extends Roo.bootstrap.Component
17601  * Bootstrap Popover class
17602  * @cfg {String} html contents of the popover   (or false to use children..)
17603  * @cfg {String} title of popover (or false to hide)
17604  * @cfg {String} placement how it is placed
17605  * @cfg {String} trigger click || hover (or false to trigger manually)
17606  * @cfg {String} over what (parent or false to trigger manually.)
17607  * @cfg {Number} delay - delay before showing
17608  
17609  * @constructor
17610  * Create a new Popover
17611  * @param {Object} config The config object
17612  */
17613
17614 Roo.bootstrap.Popover = function(config){
17615     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17616     
17617     this.addEvents({
17618         // raw events
17619          /**
17620          * @event show
17621          * After the popover show
17622          * 
17623          * @param {Roo.bootstrap.Popover} this
17624          */
17625         "show" : true,
17626         /**
17627          * @event hide
17628          * After the popover hide
17629          * 
17630          * @param {Roo.bootstrap.Popover} this
17631          */
17632         "hide" : true
17633     });
17634 };
17635
17636 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17637     
17638     title: 'Fill in a title',
17639     html: false,
17640     
17641     placement : 'right',
17642     trigger : 'hover', // hover
17643     
17644     delay : 0,
17645     
17646     over: 'parent',
17647     
17648     can_build_overlaid : false,
17649     
17650     getChildContainer : function()
17651     {
17652         return this.el.select('.popover-content',true).first();
17653     },
17654     
17655     getAutoCreate : function(){
17656          
17657         var cfg = {
17658            cls : 'popover roo-dynamic',
17659            style: 'display:block',
17660            cn : [
17661                 {
17662                     cls : 'arrow'
17663                 },
17664                 {
17665                     cls : 'popover-inner',
17666                     cn : [
17667                         {
17668                             tag: 'h3',
17669                             cls: 'popover-title popover-header',
17670                             html : this.title
17671                         },
17672                         {
17673                             cls : 'popover-content popover-body',
17674                             html : this.html
17675                         }
17676                     ]
17677                     
17678                 }
17679            ]
17680         };
17681         
17682         return cfg;
17683     },
17684     setTitle: function(str)
17685     {
17686         this.title = str;
17687         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17688     },
17689     setContent: function(str)
17690     {
17691         this.html = str;
17692         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17693     },
17694     // as it get's added to the bottom of the page.
17695     onRender : function(ct, position)
17696     {
17697         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17698         if(!this.el){
17699             var cfg = Roo.apply({},  this.getAutoCreate());
17700             cfg.id = Roo.id();
17701             
17702             if (this.cls) {
17703                 cfg.cls += ' ' + this.cls;
17704             }
17705             if (this.style) {
17706                 cfg.style = this.style;
17707             }
17708             //Roo.log("adding to ");
17709             this.el = Roo.get(document.body).createChild(cfg, position);
17710 //            Roo.log(this.el);
17711         }
17712         this.initEvents();
17713     },
17714     
17715     initEvents : function()
17716     {
17717         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17718         this.el.enableDisplayMode('block');
17719         this.el.hide();
17720         if (this.over === false) {
17721             return; 
17722         }
17723         if (this.triggers === false) {
17724             return;
17725         }
17726         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17727         var triggers = this.trigger ? this.trigger.split(' ') : [];
17728         Roo.each(triggers, function(trigger) {
17729         
17730             if (trigger == 'click') {
17731                 on_el.on('click', this.toggle, this);
17732             } else if (trigger != 'manual') {
17733                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17734                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17735       
17736                 on_el.on(eventIn  ,this.enter, this);
17737                 on_el.on(eventOut, this.leave, this);
17738             }
17739         }, this);
17740         
17741     },
17742     
17743     
17744     // private
17745     timeout : null,
17746     hoverState : null,
17747     
17748     toggle : function () {
17749         this.hoverState == 'in' ? this.leave() : this.enter();
17750     },
17751     
17752     enter : function () {
17753         
17754         clearTimeout(this.timeout);
17755     
17756         this.hoverState = 'in';
17757     
17758         if (!this.delay || !this.delay.show) {
17759             this.show();
17760             return;
17761         }
17762         var _t = this;
17763         this.timeout = setTimeout(function () {
17764             if (_t.hoverState == 'in') {
17765                 _t.show();
17766             }
17767         }, this.delay.show)
17768     },
17769     
17770     leave : function() {
17771         clearTimeout(this.timeout);
17772     
17773         this.hoverState = 'out';
17774     
17775         if (!this.delay || !this.delay.hide) {
17776             this.hide();
17777             return;
17778         }
17779         var _t = this;
17780         this.timeout = setTimeout(function () {
17781             if (_t.hoverState == 'out') {
17782                 _t.hide();
17783             }
17784         }, this.delay.hide)
17785     },
17786     
17787     show : function (on_el)
17788     {
17789         if (!on_el) {
17790             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17791         }
17792         
17793         // set content.
17794         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17795         if (this.html !== false) {
17796             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17797         }
17798         this.el.removeClass([
17799             'fade','top','bottom', 'left', 'right','in',
17800             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17801         ]);
17802         if (!this.title.length) {
17803             this.el.select('.popover-title',true).hide();
17804         }
17805         
17806         var placement = typeof this.placement == 'function' ?
17807             this.placement.call(this, this.el, on_el) :
17808             this.placement;
17809             
17810         var autoToken = /\s?auto?\s?/i;
17811         var autoPlace = autoToken.test(placement);
17812         if (autoPlace) {
17813             placement = placement.replace(autoToken, '') || 'top';
17814         }
17815         
17816         //this.el.detach()
17817         //this.el.setXY([0,0]);
17818         this.el.show();
17819         this.el.dom.style.display='block';
17820         this.el.addClass(placement);
17821         
17822         //this.el.appendTo(on_el);
17823         
17824         var p = this.getPosition();
17825         var box = this.el.getBox();
17826         
17827         if (autoPlace) {
17828             // fixme..
17829         }
17830         var align = Roo.bootstrap.Popover.alignment[placement];
17831         
17832 //        Roo.log(align);
17833         this.el.alignTo(on_el, align[0],align[1]);
17834         //var arrow = this.el.select('.arrow',true).first();
17835         //arrow.set(align[2], 
17836         
17837         this.el.addClass('in');
17838         
17839         
17840         if (this.el.hasClass('fade')) {
17841             // fade it?
17842         }
17843         
17844         this.hoverState = 'in';
17845         
17846         this.fireEvent('show', this);
17847         
17848     },
17849     hide : function()
17850     {
17851         this.el.setXY([0,0]);
17852         this.el.removeClass('in');
17853         this.el.hide();
17854         this.hoverState = null;
17855         
17856         this.fireEvent('hide', this);
17857     }
17858     
17859 });
17860
17861 Roo.bootstrap.Popover.alignment = {
17862     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17863     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17864     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17865     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17866 };
17867
17868  /*
17869  * - LGPL
17870  *
17871  * Progress
17872  * 
17873  */
17874
17875 /**
17876  * @class Roo.bootstrap.Progress
17877  * @extends Roo.bootstrap.Component
17878  * Bootstrap Progress class
17879  * @cfg {Boolean} striped striped of the progress bar
17880  * @cfg {Boolean} active animated of the progress bar
17881  * 
17882  * 
17883  * @constructor
17884  * Create a new Progress
17885  * @param {Object} config The config object
17886  */
17887
17888 Roo.bootstrap.Progress = function(config){
17889     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17890 };
17891
17892 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17893     
17894     striped : false,
17895     active: false,
17896     
17897     getAutoCreate : function(){
17898         var cfg = {
17899             tag: 'div',
17900             cls: 'progress'
17901         };
17902         
17903         
17904         if(this.striped){
17905             cfg.cls += ' progress-striped';
17906         }
17907       
17908         if(this.active){
17909             cfg.cls += ' active';
17910         }
17911         
17912         
17913         return cfg;
17914     }
17915    
17916 });
17917
17918  
17919
17920  /*
17921  * - LGPL
17922  *
17923  * ProgressBar
17924  * 
17925  */
17926
17927 /**
17928  * @class Roo.bootstrap.ProgressBar
17929  * @extends Roo.bootstrap.Component
17930  * Bootstrap ProgressBar class
17931  * @cfg {Number} aria_valuenow aria-value now
17932  * @cfg {Number} aria_valuemin aria-value min
17933  * @cfg {Number} aria_valuemax aria-value max
17934  * @cfg {String} label label for the progress bar
17935  * @cfg {String} panel (success | info | warning | danger )
17936  * @cfg {String} role role of the progress bar
17937  * @cfg {String} sr_only text
17938  * 
17939  * 
17940  * @constructor
17941  * Create a new ProgressBar
17942  * @param {Object} config The config object
17943  */
17944
17945 Roo.bootstrap.ProgressBar = function(config){
17946     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17947 };
17948
17949 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17950     
17951     aria_valuenow : 0,
17952     aria_valuemin : 0,
17953     aria_valuemax : 100,
17954     label : false,
17955     panel : false,
17956     role : false,
17957     sr_only: false,
17958     
17959     getAutoCreate : function()
17960     {
17961         
17962         var cfg = {
17963             tag: 'div',
17964             cls: 'progress-bar',
17965             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17966         };
17967         
17968         if(this.sr_only){
17969             cfg.cn = {
17970                 tag: 'span',
17971                 cls: 'sr-only',
17972                 html: this.sr_only
17973             }
17974         }
17975         
17976         if(this.role){
17977             cfg.role = this.role;
17978         }
17979         
17980         if(this.aria_valuenow){
17981             cfg['aria-valuenow'] = this.aria_valuenow;
17982         }
17983         
17984         if(this.aria_valuemin){
17985             cfg['aria-valuemin'] = this.aria_valuemin;
17986         }
17987         
17988         if(this.aria_valuemax){
17989             cfg['aria-valuemax'] = this.aria_valuemax;
17990         }
17991         
17992         if(this.label && !this.sr_only){
17993             cfg.html = this.label;
17994         }
17995         
17996         if(this.panel){
17997             cfg.cls += ' progress-bar-' + this.panel;
17998         }
17999         
18000         return cfg;
18001     },
18002     
18003     update : function(aria_valuenow)
18004     {
18005         this.aria_valuenow = aria_valuenow;
18006         
18007         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18008     }
18009    
18010 });
18011
18012  
18013
18014  /*
18015  * - LGPL
18016  *
18017  * column
18018  * 
18019  */
18020
18021 /**
18022  * @class Roo.bootstrap.TabGroup
18023  * @extends Roo.bootstrap.Column
18024  * Bootstrap Column class
18025  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18026  * @cfg {Boolean} carousel true to make the group behave like a carousel
18027  * @cfg {Boolean} bullets show bullets for the panels
18028  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18029  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18030  * @cfg {Boolean} showarrow (true|false) show arrow default true
18031  * 
18032  * @constructor
18033  * Create a new TabGroup
18034  * @param {Object} config The config object
18035  */
18036
18037 Roo.bootstrap.TabGroup = function(config){
18038     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18039     if (!this.navId) {
18040         this.navId = Roo.id();
18041     }
18042     this.tabs = [];
18043     Roo.bootstrap.TabGroup.register(this);
18044     
18045 };
18046
18047 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18048     
18049     carousel : false,
18050     transition : false,
18051     bullets : 0,
18052     timer : 0,
18053     autoslide : false,
18054     slideFn : false,
18055     slideOnTouch : false,
18056     showarrow : true,
18057     
18058     getAutoCreate : function()
18059     {
18060         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18061         
18062         cfg.cls += ' tab-content';
18063         
18064         if (this.carousel) {
18065             cfg.cls += ' carousel slide';
18066             
18067             cfg.cn = [{
18068                cls : 'carousel-inner',
18069                cn : []
18070             }];
18071         
18072             if(this.bullets  && !Roo.isTouch){
18073                 
18074                 var bullets = {
18075                     cls : 'carousel-bullets',
18076                     cn : []
18077                 };
18078                
18079                 if(this.bullets_cls){
18080                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18081                 }
18082                 
18083                 bullets.cn.push({
18084                     cls : 'clear'
18085                 });
18086                 
18087                 cfg.cn[0].cn.push(bullets);
18088             }
18089             
18090             if(this.showarrow){
18091                 cfg.cn[0].cn.push({
18092                     tag : 'div',
18093                     class : 'carousel-arrow',
18094                     cn : [
18095                         {
18096                             tag : 'div',
18097                             class : 'carousel-prev',
18098                             cn : [
18099                                 {
18100                                     tag : 'i',
18101                                     class : 'fa fa-chevron-left'
18102                                 }
18103                             ]
18104                         },
18105                         {
18106                             tag : 'div',
18107                             class : 'carousel-next',
18108                             cn : [
18109                                 {
18110                                     tag : 'i',
18111                                     class : 'fa fa-chevron-right'
18112                                 }
18113                             ]
18114                         }
18115                     ]
18116                 });
18117             }
18118             
18119         }
18120         
18121         return cfg;
18122     },
18123     
18124     initEvents:  function()
18125     {
18126 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18127 //            this.el.on("touchstart", this.onTouchStart, this);
18128 //        }
18129         
18130         if(this.autoslide){
18131             var _this = this;
18132             
18133             this.slideFn = window.setInterval(function() {
18134                 _this.showPanelNext();
18135             }, this.timer);
18136         }
18137         
18138         if(this.showarrow){
18139             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18140             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18141         }
18142         
18143         
18144     },
18145     
18146 //    onTouchStart : function(e, el, o)
18147 //    {
18148 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18149 //            return;
18150 //        }
18151 //        
18152 //        this.showPanelNext();
18153 //    },
18154     
18155     
18156     getChildContainer : function()
18157     {
18158         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18159     },
18160     
18161     /**
18162     * register a Navigation item
18163     * @param {Roo.bootstrap.NavItem} the navitem to add
18164     */
18165     register : function(item)
18166     {
18167         this.tabs.push( item);
18168         item.navId = this.navId; // not really needed..
18169         this.addBullet();
18170     
18171     },
18172     
18173     getActivePanel : function()
18174     {
18175         var r = false;
18176         Roo.each(this.tabs, function(t) {
18177             if (t.active) {
18178                 r = t;
18179                 return false;
18180             }
18181             return null;
18182         });
18183         return r;
18184         
18185     },
18186     getPanelByName : function(n)
18187     {
18188         var r = false;
18189         Roo.each(this.tabs, function(t) {
18190             if (t.tabId == n) {
18191                 r = t;
18192                 return false;
18193             }
18194             return null;
18195         });
18196         return r;
18197     },
18198     indexOfPanel : function(p)
18199     {
18200         var r = false;
18201         Roo.each(this.tabs, function(t,i) {
18202             if (t.tabId == p.tabId) {
18203                 r = i;
18204                 return false;
18205             }
18206             return null;
18207         });
18208         return r;
18209     },
18210     /**
18211      * show a specific panel
18212      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18213      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18214      */
18215     showPanel : function (pan)
18216     {
18217         if(this.transition || typeof(pan) == 'undefined'){
18218             Roo.log("waiting for the transitionend");
18219             return;
18220         }
18221         
18222         if (typeof(pan) == 'number') {
18223             pan = this.tabs[pan];
18224         }
18225         
18226         if (typeof(pan) == 'string') {
18227             pan = this.getPanelByName(pan);
18228         }
18229         
18230         var cur = this.getActivePanel();
18231         
18232         if(!pan || !cur){
18233             Roo.log('pan or acitve pan is undefined');
18234             return false;
18235         }
18236         
18237         if (pan.tabId == this.getActivePanel().tabId) {
18238             return true;
18239         }
18240         
18241         if (false === cur.fireEvent('beforedeactivate')) {
18242             return false;
18243         }
18244         
18245         if(this.bullets > 0 && !Roo.isTouch){
18246             this.setActiveBullet(this.indexOfPanel(pan));
18247         }
18248         
18249         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18250             
18251             this.transition = true;
18252             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18253             var lr = dir == 'next' ? 'left' : 'right';
18254             pan.el.addClass(dir); // or prev
18255             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18256             cur.el.addClass(lr); // or right
18257             pan.el.addClass(lr);
18258             
18259             var _this = this;
18260             cur.el.on('transitionend', function() {
18261                 Roo.log("trans end?");
18262                 
18263                 pan.el.removeClass([lr,dir]);
18264                 pan.setActive(true);
18265                 
18266                 cur.el.removeClass([lr]);
18267                 cur.setActive(false);
18268                 
18269                 _this.transition = false;
18270                 
18271             }, this, { single:  true } );
18272             
18273             return true;
18274         }
18275         
18276         cur.setActive(false);
18277         pan.setActive(true);
18278         
18279         return true;
18280         
18281     },
18282     showPanelNext : function()
18283     {
18284         var i = this.indexOfPanel(this.getActivePanel());
18285         
18286         if (i >= this.tabs.length - 1 && !this.autoslide) {
18287             return;
18288         }
18289         
18290         if (i >= this.tabs.length - 1 && this.autoslide) {
18291             i = -1;
18292         }
18293         
18294         this.showPanel(this.tabs[i+1]);
18295     },
18296     
18297     showPanelPrev : function()
18298     {
18299         var i = this.indexOfPanel(this.getActivePanel());
18300         
18301         if (i  < 1 && !this.autoslide) {
18302             return;
18303         }
18304         
18305         if (i < 1 && this.autoslide) {
18306             i = this.tabs.length;
18307         }
18308         
18309         this.showPanel(this.tabs[i-1]);
18310     },
18311     
18312     
18313     addBullet: function()
18314     {
18315         if(!this.bullets || Roo.isTouch){
18316             return;
18317         }
18318         var ctr = this.el.select('.carousel-bullets',true).first();
18319         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18320         var bullet = ctr.createChild({
18321             cls : 'bullet bullet-' + i
18322         },ctr.dom.lastChild);
18323         
18324         
18325         var _this = this;
18326         
18327         bullet.on('click', (function(e, el, o, ii, t){
18328
18329             e.preventDefault();
18330
18331             this.showPanel(ii);
18332
18333             if(this.autoslide && this.slideFn){
18334                 clearInterval(this.slideFn);
18335                 this.slideFn = window.setInterval(function() {
18336                     _this.showPanelNext();
18337                 }, this.timer);
18338             }
18339
18340         }).createDelegate(this, [i, bullet], true));
18341                 
18342         
18343     },
18344      
18345     setActiveBullet : function(i)
18346     {
18347         if(Roo.isTouch){
18348             return;
18349         }
18350         
18351         Roo.each(this.el.select('.bullet', true).elements, function(el){
18352             el.removeClass('selected');
18353         });
18354
18355         var bullet = this.el.select('.bullet-' + i, true).first();
18356         
18357         if(!bullet){
18358             return;
18359         }
18360         
18361         bullet.addClass('selected');
18362     }
18363     
18364     
18365   
18366 });
18367
18368  
18369
18370  
18371  
18372 Roo.apply(Roo.bootstrap.TabGroup, {
18373     
18374     groups: {},
18375      /**
18376     * register a Navigation Group
18377     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18378     */
18379     register : function(navgrp)
18380     {
18381         this.groups[navgrp.navId] = navgrp;
18382         
18383     },
18384     /**
18385     * fetch a Navigation Group based on the navigation ID
18386     * if one does not exist , it will get created.
18387     * @param {string} the navgroup to add
18388     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18389     */
18390     get: function(navId) {
18391         if (typeof(this.groups[navId]) == 'undefined') {
18392             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18393         }
18394         return this.groups[navId] ;
18395     }
18396     
18397     
18398     
18399 });
18400
18401  /*
18402  * - LGPL
18403  *
18404  * TabPanel
18405  * 
18406  */
18407
18408 /**
18409  * @class Roo.bootstrap.TabPanel
18410  * @extends Roo.bootstrap.Component
18411  * Bootstrap TabPanel class
18412  * @cfg {Boolean} active panel active
18413  * @cfg {String} html panel content
18414  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18415  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18416  * @cfg {String} href click to link..
18417  * 
18418  * 
18419  * @constructor
18420  * Create a new TabPanel
18421  * @param {Object} config The config object
18422  */
18423
18424 Roo.bootstrap.TabPanel = function(config){
18425     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18426     this.addEvents({
18427         /**
18428              * @event changed
18429              * Fires when the active status changes
18430              * @param {Roo.bootstrap.TabPanel} this
18431              * @param {Boolean} state the new state
18432             
18433          */
18434         'changed': true,
18435         /**
18436              * @event beforedeactivate
18437              * Fires before a tab is de-activated - can be used to do validation on a form.
18438              * @param {Roo.bootstrap.TabPanel} this
18439              * @return {Boolean} false if there is an error
18440             
18441          */
18442         'beforedeactivate': true
18443      });
18444     
18445     this.tabId = this.tabId || Roo.id();
18446   
18447 };
18448
18449 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18450     
18451     active: false,
18452     html: false,
18453     tabId: false,
18454     navId : false,
18455     href : '',
18456     
18457     getAutoCreate : function(){
18458         var cfg = {
18459             tag: 'div',
18460             // item is needed for carousel - not sure if it has any effect otherwise
18461             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18462             html: this.html || ''
18463         };
18464         
18465         if(this.active){
18466             cfg.cls += ' active';
18467         }
18468         
18469         if(this.tabId){
18470             cfg.tabId = this.tabId;
18471         }
18472         
18473         
18474         return cfg;
18475     },
18476     
18477     initEvents:  function()
18478     {
18479         var p = this.parent();
18480         
18481         this.navId = this.navId || p.navId;
18482         
18483         if (typeof(this.navId) != 'undefined') {
18484             // not really needed.. but just in case.. parent should be a NavGroup.
18485             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18486             
18487             tg.register(this);
18488             
18489             var i = tg.tabs.length - 1;
18490             
18491             if(this.active && tg.bullets > 0 && i < tg.bullets){
18492                 tg.setActiveBullet(i);
18493             }
18494         }
18495         
18496         this.el.on('click', this.onClick, this);
18497         
18498         if(Roo.isTouch){
18499             this.el.on("touchstart", this.onTouchStart, this);
18500             this.el.on("touchmove", this.onTouchMove, this);
18501             this.el.on("touchend", this.onTouchEnd, this);
18502         }
18503         
18504     },
18505     
18506     onRender : function(ct, position)
18507     {
18508         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18509     },
18510     
18511     setActive : function(state)
18512     {
18513         Roo.log("panel - set active " + this.tabId + "=" + state);
18514         
18515         this.active = state;
18516         if (!state) {
18517             this.el.removeClass('active');
18518             
18519         } else  if (!this.el.hasClass('active')) {
18520             this.el.addClass('active');
18521         }
18522         
18523         this.fireEvent('changed', this, state);
18524     },
18525     
18526     onClick : function(e)
18527     {
18528         e.preventDefault();
18529         
18530         if(!this.href.length){
18531             return;
18532         }
18533         
18534         window.location.href = this.href;
18535     },
18536     
18537     startX : 0,
18538     startY : 0,
18539     endX : 0,
18540     endY : 0,
18541     swiping : false,
18542     
18543     onTouchStart : function(e)
18544     {
18545         this.swiping = false;
18546         
18547         this.startX = e.browserEvent.touches[0].clientX;
18548         this.startY = e.browserEvent.touches[0].clientY;
18549     },
18550     
18551     onTouchMove : function(e)
18552     {
18553         this.swiping = true;
18554         
18555         this.endX = e.browserEvent.touches[0].clientX;
18556         this.endY = e.browserEvent.touches[0].clientY;
18557     },
18558     
18559     onTouchEnd : function(e)
18560     {
18561         if(!this.swiping){
18562             this.onClick(e);
18563             return;
18564         }
18565         
18566         var tabGroup = this.parent();
18567         
18568         if(this.endX > this.startX){ // swiping right
18569             tabGroup.showPanelPrev();
18570             return;
18571         }
18572         
18573         if(this.startX > this.endX){ // swiping left
18574             tabGroup.showPanelNext();
18575             return;
18576         }
18577     }
18578     
18579     
18580 });
18581  
18582
18583  
18584
18585  /*
18586  * - LGPL
18587  *
18588  * DateField
18589  * 
18590  */
18591
18592 /**
18593  * @class Roo.bootstrap.DateField
18594  * @extends Roo.bootstrap.Input
18595  * Bootstrap DateField class
18596  * @cfg {Number} weekStart default 0
18597  * @cfg {String} viewMode default empty, (months|years)
18598  * @cfg {String} minViewMode default empty, (months|years)
18599  * @cfg {Number} startDate default -Infinity
18600  * @cfg {Number} endDate default Infinity
18601  * @cfg {Boolean} todayHighlight default false
18602  * @cfg {Boolean} todayBtn default false
18603  * @cfg {Boolean} calendarWeeks default false
18604  * @cfg {Object} daysOfWeekDisabled default empty
18605  * @cfg {Boolean} singleMode default false (true | false)
18606  * 
18607  * @cfg {Boolean} keyboardNavigation default true
18608  * @cfg {String} language default en
18609  * 
18610  * @constructor
18611  * Create a new DateField
18612  * @param {Object} config The config object
18613  */
18614
18615 Roo.bootstrap.DateField = function(config){
18616     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18617      this.addEvents({
18618             /**
18619              * @event show
18620              * Fires when this field show.
18621              * @param {Roo.bootstrap.DateField} this
18622              * @param {Mixed} date The date value
18623              */
18624             show : true,
18625             /**
18626              * @event show
18627              * Fires when this field hide.
18628              * @param {Roo.bootstrap.DateField} this
18629              * @param {Mixed} date The date value
18630              */
18631             hide : true,
18632             /**
18633              * @event select
18634              * Fires when select a date.
18635              * @param {Roo.bootstrap.DateField} this
18636              * @param {Mixed} date The date value
18637              */
18638             select : true,
18639             /**
18640              * @event beforeselect
18641              * Fires when before select a date.
18642              * @param {Roo.bootstrap.DateField} this
18643              * @param {Mixed} date The date value
18644              */
18645             beforeselect : true
18646         });
18647 };
18648
18649 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18650     
18651     /**
18652      * @cfg {String} format
18653      * The default date format string which can be overriden for localization support.  The format must be
18654      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18655      */
18656     format : "m/d/y",
18657     /**
18658      * @cfg {String} altFormats
18659      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18660      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18661      */
18662     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18663     
18664     weekStart : 0,
18665     
18666     viewMode : '',
18667     
18668     minViewMode : '',
18669     
18670     todayHighlight : false,
18671     
18672     todayBtn: false,
18673     
18674     language: 'en',
18675     
18676     keyboardNavigation: true,
18677     
18678     calendarWeeks: false,
18679     
18680     startDate: -Infinity,
18681     
18682     endDate: Infinity,
18683     
18684     daysOfWeekDisabled: [],
18685     
18686     _events: [],
18687     
18688     singleMode : false,
18689     
18690     UTCDate: function()
18691     {
18692         return new Date(Date.UTC.apply(Date, arguments));
18693     },
18694     
18695     UTCToday: function()
18696     {
18697         var today = new Date();
18698         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18699     },
18700     
18701     getDate: function() {
18702             var d = this.getUTCDate();
18703             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18704     },
18705     
18706     getUTCDate: function() {
18707             return this.date;
18708     },
18709     
18710     setDate: function(d) {
18711             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18712     },
18713     
18714     setUTCDate: function(d) {
18715             this.date = d;
18716             this.setValue(this.formatDate(this.date));
18717     },
18718         
18719     onRender: function(ct, position)
18720     {
18721         
18722         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18723         
18724         this.language = this.language || 'en';
18725         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18726         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18727         
18728         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18729         this.format = this.format || 'm/d/y';
18730         this.isInline = false;
18731         this.isInput = true;
18732         this.component = this.el.select('.add-on', true).first() || false;
18733         this.component = (this.component && this.component.length === 0) ? false : this.component;
18734         this.hasInput = this.component && this.inputEl().length;
18735         
18736         if (typeof(this.minViewMode === 'string')) {
18737             switch (this.minViewMode) {
18738                 case 'months':
18739                     this.minViewMode = 1;
18740                     break;
18741                 case 'years':
18742                     this.minViewMode = 2;
18743                     break;
18744                 default:
18745                     this.minViewMode = 0;
18746                     break;
18747             }
18748         }
18749         
18750         if (typeof(this.viewMode === 'string')) {
18751             switch (this.viewMode) {
18752                 case 'months':
18753                     this.viewMode = 1;
18754                     break;
18755                 case 'years':
18756                     this.viewMode = 2;
18757                     break;
18758                 default:
18759                     this.viewMode = 0;
18760                     break;
18761             }
18762         }
18763                 
18764         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18765         
18766 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18767         
18768         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18769         
18770         this.picker().on('mousedown', this.onMousedown, this);
18771         this.picker().on('click', this.onClick, this);
18772         
18773         this.picker().addClass('datepicker-dropdown');
18774         
18775         this.startViewMode = this.viewMode;
18776         
18777         if(this.singleMode){
18778             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18779                 v.setVisibilityMode(Roo.Element.DISPLAY);
18780                 v.hide();
18781             });
18782             
18783             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18784                 v.setStyle('width', '189px');
18785             });
18786         }
18787         
18788         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18789             if(!this.calendarWeeks){
18790                 v.remove();
18791                 return;
18792             }
18793             
18794             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18795             v.attr('colspan', function(i, val){
18796                 return parseInt(val) + 1;
18797             });
18798         });
18799                         
18800         
18801         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18802         
18803         this.setStartDate(this.startDate);
18804         this.setEndDate(this.endDate);
18805         
18806         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18807         
18808         this.fillDow();
18809         this.fillMonths();
18810         this.update();
18811         this.showMode();
18812         
18813         if(this.isInline) {
18814             this.showPopup();
18815         }
18816     },
18817     
18818     picker : function()
18819     {
18820         return this.pickerEl;
18821 //        return this.el.select('.datepicker', true).first();
18822     },
18823     
18824     fillDow: function()
18825     {
18826         var dowCnt = this.weekStart;
18827         
18828         var dow = {
18829             tag: 'tr',
18830             cn: [
18831                 
18832             ]
18833         };
18834         
18835         if(this.calendarWeeks){
18836             dow.cn.push({
18837                 tag: 'th',
18838                 cls: 'cw',
18839                 html: '&nbsp;'
18840             })
18841         }
18842         
18843         while (dowCnt < this.weekStart + 7) {
18844             dow.cn.push({
18845                 tag: 'th',
18846                 cls: 'dow',
18847                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18848             });
18849         }
18850         
18851         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18852     },
18853     
18854     fillMonths: function()
18855     {    
18856         var i = 0;
18857         var months = this.picker().select('>.datepicker-months td', true).first();
18858         
18859         months.dom.innerHTML = '';
18860         
18861         while (i < 12) {
18862             var month = {
18863                 tag: 'span',
18864                 cls: 'month',
18865                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18866             };
18867             
18868             months.createChild(month);
18869         }
18870         
18871     },
18872     
18873     update: function()
18874     {
18875         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;
18876         
18877         if (this.date < this.startDate) {
18878             this.viewDate = new Date(this.startDate);
18879         } else if (this.date > this.endDate) {
18880             this.viewDate = new Date(this.endDate);
18881         } else {
18882             this.viewDate = new Date(this.date);
18883         }
18884         
18885         this.fill();
18886     },
18887     
18888     fill: function() 
18889     {
18890         var d = new Date(this.viewDate),
18891                 year = d.getUTCFullYear(),
18892                 month = d.getUTCMonth(),
18893                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18894                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18895                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18896                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18897                 currentDate = this.date && this.date.valueOf(),
18898                 today = this.UTCToday();
18899         
18900         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18901         
18902 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18903         
18904 //        this.picker.select('>tfoot th.today').
18905 //                                              .text(dates[this.language].today)
18906 //                                              .toggle(this.todayBtn !== false);
18907     
18908         this.updateNavArrows();
18909         this.fillMonths();
18910                                                 
18911         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18912         
18913         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18914          
18915         prevMonth.setUTCDate(day);
18916         
18917         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18918         
18919         var nextMonth = new Date(prevMonth);
18920         
18921         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18922         
18923         nextMonth = nextMonth.valueOf();
18924         
18925         var fillMonths = false;
18926         
18927         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18928         
18929         while(prevMonth.valueOf() <= nextMonth) {
18930             var clsName = '';
18931             
18932             if (prevMonth.getUTCDay() === this.weekStart) {
18933                 if(fillMonths){
18934                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18935                 }
18936                     
18937                 fillMonths = {
18938                     tag: 'tr',
18939                     cn: []
18940                 };
18941                 
18942                 if(this.calendarWeeks){
18943                     // ISO 8601: First week contains first thursday.
18944                     // ISO also states week starts on Monday, but we can be more abstract here.
18945                     var
18946                     // Start of current week: based on weekstart/current date
18947                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18948                     // Thursday of this week
18949                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18950                     // First Thursday of year, year from thursday
18951                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18952                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18953                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18954                     
18955                     fillMonths.cn.push({
18956                         tag: 'td',
18957                         cls: 'cw',
18958                         html: calWeek
18959                     });
18960                 }
18961             }
18962             
18963             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18964                 clsName += ' old';
18965             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18966                 clsName += ' new';
18967             }
18968             if (this.todayHighlight &&
18969                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18970                 prevMonth.getUTCMonth() == today.getMonth() &&
18971                 prevMonth.getUTCDate() == today.getDate()) {
18972                 clsName += ' today';
18973             }
18974             
18975             if (currentDate && prevMonth.valueOf() === currentDate) {
18976                 clsName += ' active';
18977             }
18978             
18979             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18980                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18981                     clsName += ' disabled';
18982             }
18983             
18984             fillMonths.cn.push({
18985                 tag: 'td',
18986                 cls: 'day ' + clsName,
18987                 html: prevMonth.getDate()
18988             });
18989             
18990             prevMonth.setDate(prevMonth.getDate()+1);
18991         }
18992           
18993         var currentYear = this.date && this.date.getUTCFullYear();
18994         var currentMonth = this.date && this.date.getUTCMonth();
18995         
18996         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18997         
18998         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18999             v.removeClass('active');
19000             
19001             if(currentYear === year && k === currentMonth){
19002                 v.addClass('active');
19003             }
19004             
19005             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19006                 v.addClass('disabled');
19007             }
19008             
19009         });
19010         
19011         
19012         year = parseInt(year/10, 10) * 10;
19013         
19014         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19015         
19016         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19017         
19018         year -= 1;
19019         for (var i = -1; i < 11; i++) {
19020             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19021                 tag: 'span',
19022                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19023                 html: year
19024             });
19025             
19026             year += 1;
19027         }
19028     },
19029     
19030     showMode: function(dir) 
19031     {
19032         if (dir) {
19033             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19034         }
19035         
19036         Roo.each(this.picker().select('>div',true).elements, function(v){
19037             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19038             v.hide();
19039         });
19040         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19041     },
19042     
19043     place: function()
19044     {
19045         if(this.isInline) {
19046             return;
19047         }
19048         
19049         this.picker().removeClass(['bottom', 'top']);
19050         
19051         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19052             /*
19053              * place to the top of element!
19054              *
19055              */
19056             
19057             this.picker().addClass('top');
19058             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19059             
19060             return;
19061         }
19062         
19063         this.picker().addClass('bottom');
19064         
19065         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19066     },
19067     
19068     parseDate : function(value)
19069     {
19070         if(!value || value instanceof Date){
19071             return value;
19072         }
19073         var v = Date.parseDate(value, this.format);
19074         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19075             v = Date.parseDate(value, 'Y-m-d');
19076         }
19077         if(!v && this.altFormats){
19078             if(!this.altFormatsArray){
19079                 this.altFormatsArray = this.altFormats.split("|");
19080             }
19081             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19082                 v = Date.parseDate(value, this.altFormatsArray[i]);
19083             }
19084         }
19085         return v;
19086     },
19087     
19088     formatDate : function(date, fmt)
19089     {   
19090         return (!date || !(date instanceof Date)) ?
19091         date : date.dateFormat(fmt || this.format);
19092     },
19093     
19094     onFocus : function()
19095     {
19096         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19097         this.showPopup();
19098     },
19099     
19100     onBlur : function()
19101     {
19102         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19103         
19104         var d = this.inputEl().getValue();
19105         
19106         this.setValue(d);
19107                 
19108         this.hidePopup();
19109     },
19110     
19111     showPopup : function()
19112     {
19113         this.picker().show();
19114         this.update();
19115         this.place();
19116         
19117         this.fireEvent('showpopup', this, this.date);
19118     },
19119     
19120     hidePopup : function()
19121     {
19122         if(this.isInline) {
19123             return;
19124         }
19125         this.picker().hide();
19126         this.viewMode = this.startViewMode;
19127         this.showMode();
19128         
19129         this.fireEvent('hidepopup', this, this.date);
19130         
19131     },
19132     
19133     onMousedown: function(e)
19134     {
19135         e.stopPropagation();
19136         e.preventDefault();
19137     },
19138     
19139     keyup: function(e)
19140     {
19141         Roo.bootstrap.DateField.superclass.keyup.call(this);
19142         this.update();
19143     },
19144
19145     setValue: function(v)
19146     {
19147         if(this.fireEvent('beforeselect', this, v) !== false){
19148             var d = new Date(this.parseDate(v) ).clearTime();
19149         
19150             if(isNaN(d.getTime())){
19151                 this.date = this.viewDate = '';
19152                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19153                 return;
19154             }
19155
19156             v = this.formatDate(d);
19157
19158             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19159
19160             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19161
19162             this.update();
19163
19164             this.fireEvent('select', this, this.date);
19165         }
19166     },
19167     
19168     getValue: function()
19169     {
19170         return this.formatDate(this.date);
19171     },
19172     
19173     fireKey: function(e)
19174     {
19175         if (!this.picker().isVisible()){
19176             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19177                 this.showPopup();
19178             }
19179             return;
19180         }
19181         
19182         var dateChanged = false,
19183         dir, day, month,
19184         newDate, newViewDate;
19185         
19186         switch(e.keyCode){
19187             case 27: // escape
19188                 this.hidePopup();
19189                 e.preventDefault();
19190                 break;
19191             case 37: // left
19192             case 39: // right
19193                 if (!this.keyboardNavigation) {
19194                     break;
19195                 }
19196                 dir = e.keyCode == 37 ? -1 : 1;
19197                 
19198                 if (e.ctrlKey){
19199                     newDate = this.moveYear(this.date, dir);
19200                     newViewDate = this.moveYear(this.viewDate, dir);
19201                 } else if (e.shiftKey){
19202                     newDate = this.moveMonth(this.date, dir);
19203                     newViewDate = this.moveMonth(this.viewDate, dir);
19204                 } else {
19205                     newDate = new Date(this.date);
19206                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19207                     newViewDate = new Date(this.viewDate);
19208                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19209                 }
19210                 if (this.dateWithinRange(newDate)){
19211                     this.date = newDate;
19212                     this.viewDate = newViewDate;
19213                     this.setValue(this.formatDate(this.date));
19214 //                    this.update();
19215                     e.preventDefault();
19216                     dateChanged = true;
19217                 }
19218                 break;
19219             case 38: // up
19220             case 40: // down
19221                 if (!this.keyboardNavigation) {
19222                     break;
19223                 }
19224                 dir = e.keyCode == 38 ? -1 : 1;
19225                 if (e.ctrlKey){
19226                     newDate = this.moveYear(this.date, dir);
19227                     newViewDate = this.moveYear(this.viewDate, dir);
19228                 } else if (e.shiftKey){
19229                     newDate = this.moveMonth(this.date, dir);
19230                     newViewDate = this.moveMonth(this.viewDate, dir);
19231                 } else {
19232                     newDate = new Date(this.date);
19233                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19234                     newViewDate = new Date(this.viewDate);
19235                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19236                 }
19237                 if (this.dateWithinRange(newDate)){
19238                     this.date = newDate;
19239                     this.viewDate = newViewDate;
19240                     this.setValue(this.formatDate(this.date));
19241 //                    this.update();
19242                     e.preventDefault();
19243                     dateChanged = true;
19244                 }
19245                 break;
19246             case 13: // enter
19247                 this.setValue(this.formatDate(this.date));
19248                 this.hidePopup();
19249                 e.preventDefault();
19250                 break;
19251             case 9: // tab
19252                 this.setValue(this.formatDate(this.date));
19253                 this.hidePopup();
19254                 break;
19255             case 16: // shift
19256             case 17: // ctrl
19257             case 18: // alt
19258                 break;
19259             default :
19260                 this.hidePopup();
19261                 
19262         }
19263     },
19264     
19265     
19266     onClick: function(e) 
19267     {
19268         e.stopPropagation();
19269         e.preventDefault();
19270         
19271         var target = e.getTarget();
19272         
19273         if(target.nodeName.toLowerCase() === 'i'){
19274             target = Roo.get(target).dom.parentNode;
19275         }
19276         
19277         var nodeName = target.nodeName;
19278         var className = target.className;
19279         var html = target.innerHTML;
19280         //Roo.log(nodeName);
19281         
19282         switch(nodeName.toLowerCase()) {
19283             case 'th':
19284                 switch(className) {
19285                     case 'switch':
19286                         this.showMode(1);
19287                         break;
19288                     case 'prev':
19289                     case 'next':
19290                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19291                         switch(this.viewMode){
19292                                 case 0:
19293                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19294                                         break;
19295                                 case 1:
19296                                 case 2:
19297                                         this.viewDate = this.moveYear(this.viewDate, dir);
19298                                         break;
19299                         }
19300                         this.fill();
19301                         break;
19302                     case 'today':
19303                         var date = new Date();
19304                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19305 //                        this.fill()
19306                         this.setValue(this.formatDate(this.date));
19307                         
19308                         this.hidePopup();
19309                         break;
19310                 }
19311                 break;
19312             case 'span':
19313                 if (className.indexOf('disabled') < 0) {
19314                     this.viewDate.setUTCDate(1);
19315                     if (className.indexOf('month') > -1) {
19316                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19317                     } else {
19318                         var year = parseInt(html, 10) || 0;
19319                         this.viewDate.setUTCFullYear(year);
19320                         
19321                     }
19322                     
19323                     if(this.singleMode){
19324                         this.setValue(this.formatDate(this.viewDate));
19325                         this.hidePopup();
19326                         return;
19327                     }
19328                     
19329                     this.showMode(-1);
19330                     this.fill();
19331                 }
19332                 break;
19333                 
19334             case 'td':
19335                 //Roo.log(className);
19336                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19337                     var day = parseInt(html, 10) || 1;
19338                     var year = this.viewDate.getUTCFullYear(),
19339                         month = this.viewDate.getUTCMonth();
19340
19341                     if (className.indexOf('old') > -1) {
19342                         if(month === 0 ){
19343                             month = 11;
19344                             year -= 1;
19345                         }else{
19346                             month -= 1;
19347                         }
19348                     } else if (className.indexOf('new') > -1) {
19349                         if (month == 11) {
19350                             month = 0;
19351                             year += 1;
19352                         } else {
19353                             month += 1;
19354                         }
19355                     }
19356                     //Roo.log([year,month,day]);
19357                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19358                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19359 //                    this.fill();
19360                     //Roo.log(this.formatDate(this.date));
19361                     this.setValue(this.formatDate(this.date));
19362                     this.hidePopup();
19363                 }
19364                 break;
19365         }
19366     },
19367     
19368     setStartDate: function(startDate)
19369     {
19370         this.startDate = startDate || -Infinity;
19371         if (this.startDate !== -Infinity) {
19372             this.startDate = this.parseDate(this.startDate);
19373         }
19374         this.update();
19375         this.updateNavArrows();
19376     },
19377
19378     setEndDate: function(endDate)
19379     {
19380         this.endDate = endDate || Infinity;
19381         if (this.endDate !== Infinity) {
19382             this.endDate = this.parseDate(this.endDate);
19383         }
19384         this.update();
19385         this.updateNavArrows();
19386     },
19387     
19388     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19389     {
19390         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19391         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19392             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19393         }
19394         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19395             return parseInt(d, 10);
19396         });
19397         this.update();
19398         this.updateNavArrows();
19399     },
19400     
19401     updateNavArrows: function() 
19402     {
19403         if(this.singleMode){
19404             return;
19405         }
19406         
19407         var d = new Date(this.viewDate),
19408         year = d.getUTCFullYear(),
19409         month = d.getUTCMonth();
19410         
19411         Roo.each(this.picker().select('.prev', true).elements, function(v){
19412             v.show();
19413             switch (this.viewMode) {
19414                 case 0:
19415
19416                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19417                         v.hide();
19418                     }
19419                     break;
19420                 case 1:
19421                 case 2:
19422                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19423                         v.hide();
19424                     }
19425                     break;
19426             }
19427         });
19428         
19429         Roo.each(this.picker().select('.next', true).elements, function(v){
19430             v.show();
19431             switch (this.viewMode) {
19432                 case 0:
19433
19434                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19435                         v.hide();
19436                     }
19437                     break;
19438                 case 1:
19439                 case 2:
19440                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19441                         v.hide();
19442                     }
19443                     break;
19444             }
19445         })
19446     },
19447     
19448     moveMonth: function(date, dir)
19449     {
19450         if (!dir) {
19451             return date;
19452         }
19453         var new_date = new Date(date.valueOf()),
19454         day = new_date.getUTCDate(),
19455         month = new_date.getUTCMonth(),
19456         mag = Math.abs(dir),
19457         new_month, test;
19458         dir = dir > 0 ? 1 : -1;
19459         if (mag == 1){
19460             test = dir == -1
19461             // If going back one month, make sure month is not current month
19462             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19463             ? function(){
19464                 return new_date.getUTCMonth() == month;
19465             }
19466             // If going forward one month, make sure month is as expected
19467             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19468             : function(){
19469                 return new_date.getUTCMonth() != new_month;
19470             };
19471             new_month = month + dir;
19472             new_date.setUTCMonth(new_month);
19473             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19474             if (new_month < 0 || new_month > 11) {
19475                 new_month = (new_month + 12) % 12;
19476             }
19477         } else {
19478             // For magnitudes >1, move one month at a time...
19479             for (var i=0; i<mag; i++) {
19480                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19481                 new_date = this.moveMonth(new_date, dir);
19482             }
19483             // ...then reset the day, keeping it in the new month
19484             new_month = new_date.getUTCMonth();
19485             new_date.setUTCDate(day);
19486             test = function(){
19487                 return new_month != new_date.getUTCMonth();
19488             };
19489         }
19490         // Common date-resetting loop -- if date is beyond end of month, make it
19491         // end of month
19492         while (test()){
19493             new_date.setUTCDate(--day);
19494             new_date.setUTCMonth(new_month);
19495         }
19496         return new_date;
19497     },
19498
19499     moveYear: function(date, dir)
19500     {
19501         return this.moveMonth(date, dir*12);
19502     },
19503
19504     dateWithinRange: function(date)
19505     {
19506         return date >= this.startDate && date <= this.endDate;
19507     },
19508
19509     
19510     remove: function() 
19511     {
19512         this.picker().remove();
19513     },
19514     
19515     validateValue : function(value)
19516     {
19517         if(this.getVisibilityEl().hasClass('hidden')){
19518             return true;
19519         }
19520         
19521         if(value.length < 1)  {
19522             if(this.allowBlank){
19523                 return true;
19524             }
19525             return false;
19526         }
19527         
19528         if(value.length < this.minLength){
19529             return false;
19530         }
19531         if(value.length > this.maxLength){
19532             return false;
19533         }
19534         if(this.vtype){
19535             var vt = Roo.form.VTypes;
19536             if(!vt[this.vtype](value, this)){
19537                 return false;
19538             }
19539         }
19540         if(typeof this.validator == "function"){
19541             var msg = this.validator(value);
19542             if(msg !== true){
19543                 return false;
19544             }
19545         }
19546         
19547         if(this.regex && !this.regex.test(value)){
19548             return false;
19549         }
19550         
19551         if(typeof(this.parseDate(value)) == 'undefined'){
19552             return false;
19553         }
19554         
19555         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19556             return false;
19557         }      
19558         
19559         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19560             return false;
19561         } 
19562         
19563         
19564         return true;
19565     },
19566     
19567     reset : function()
19568     {
19569         this.date = this.viewDate = '';
19570         
19571         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19572     }
19573    
19574 });
19575
19576 Roo.apply(Roo.bootstrap.DateField,  {
19577     
19578     head : {
19579         tag: 'thead',
19580         cn: [
19581         {
19582             tag: 'tr',
19583             cn: [
19584             {
19585                 tag: 'th',
19586                 cls: 'prev',
19587                 html: '<i class="fa fa-arrow-left"/>'
19588             },
19589             {
19590                 tag: 'th',
19591                 cls: 'switch',
19592                 colspan: '5'
19593             },
19594             {
19595                 tag: 'th',
19596                 cls: 'next',
19597                 html: '<i class="fa fa-arrow-right"/>'
19598             }
19599
19600             ]
19601         }
19602         ]
19603     },
19604     
19605     content : {
19606         tag: 'tbody',
19607         cn: [
19608         {
19609             tag: 'tr',
19610             cn: [
19611             {
19612                 tag: 'td',
19613                 colspan: '7'
19614             }
19615             ]
19616         }
19617         ]
19618     },
19619     
19620     footer : {
19621         tag: 'tfoot',
19622         cn: [
19623         {
19624             tag: 'tr',
19625             cn: [
19626             {
19627                 tag: 'th',
19628                 colspan: '7',
19629                 cls: 'today'
19630             }
19631                     
19632             ]
19633         }
19634         ]
19635     },
19636     
19637     dates:{
19638         en: {
19639             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19640             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19641             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19642             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19643             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19644             today: "Today"
19645         }
19646     },
19647     
19648     modes: [
19649     {
19650         clsName: 'days',
19651         navFnc: 'Month',
19652         navStep: 1
19653     },
19654     {
19655         clsName: 'months',
19656         navFnc: 'FullYear',
19657         navStep: 1
19658     },
19659     {
19660         clsName: 'years',
19661         navFnc: 'FullYear',
19662         navStep: 10
19663     }]
19664 });
19665
19666 Roo.apply(Roo.bootstrap.DateField,  {
19667   
19668     template : {
19669         tag: 'div',
19670         cls: 'datepicker dropdown-menu roo-dynamic',
19671         cn: [
19672         {
19673             tag: 'div',
19674             cls: 'datepicker-days',
19675             cn: [
19676             {
19677                 tag: 'table',
19678                 cls: 'table-condensed',
19679                 cn:[
19680                 Roo.bootstrap.DateField.head,
19681                 {
19682                     tag: 'tbody'
19683                 },
19684                 Roo.bootstrap.DateField.footer
19685                 ]
19686             }
19687             ]
19688         },
19689         {
19690             tag: 'div',
19691             cls: 'datepicker-months',
19692             cn: [
19693             {
19694                 tag: 'table',
19695                 cls: 'table-condensed',
19696                 cn:[
19697                 Roo.bootstrap.DateField.head,
19698                 Roo.bootstrap.DateField.content,
19699                 Roo.bootstrap.DateField.footer
19700                 ]
19701             }
19702             ]
19703         },
19704         {
19705             tag: 'div',
19706             cls: 'datepicker-years',
19707             cn: [
19708             {
19709                 tag: 'table',
19710                 cls: 'table-condensed',
19711                 cn:[
19712                 Roo.bootstrap.DateField.head,
19713                 Roo.bootstrap.DateField.content,
19714                 Roo.bootstrap.DateField.footer
19715                 ]
19716             }
19717             ]
19718         }
19719         ]
19720     }
19721 });
19722
19723  
19724
19725  /*
19726  * - LGPL
19727  *
19728  * TimeField
19729  * 
19730  */
19731
19732 /**
19733  * @class Roo.bootstrap.TimeField
19734  * @extends Roo.bootstrap.Input
19735  * Bootstrap DateField class
19736  * 
19737  * 
19738  * @constructor
19739  * Create a new TimeField
19740  * @param {Object} config The config object
19741  */
19742
19743 Roo.bootstrap.TimeField = function(config){
19744     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19745     this.addEvents({
19746             /**
19747              * @event show
19748              * Fires when this field show.
19749              * @param {Roo.bootstrap.DateField} thisthis
19750              * @param {Mixed} date The date value
19751              */
19752             show : true,
19753             /**
19754              * @event show
19755              * Fires when this field hide.
19756              * @param {Roo.bootstrap.DateField} this
19757              * @param {Mixed} date The date value
19758              */
19759             hide : true,
19760             /**
19761              * @event select
19762              * Fires when select a date.
19763              * @param {Roo.bootstrap.DateField} this
19764              * @param {Mixed} date The date value
19765              */
19766             select : true
19767         });
19768 };
19769
19770 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19771     
19772     /**
19773      * @cfg {String} format
19774      * The default time format string which can be overriden for localization support.  The format must be
19775      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19776      */
19777     format : "H:i",
19778        
19779     onRender: function(ct, position)
19780     {
19781         
19782         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19783                 
19784         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19785         
19786         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19787         
19788         this.pop = this.picker().select('>.datepicker-time',true).first();
19789         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19790         
19791         this.picker().on('mousedown', this.onMousedown, this);
19792         this.picker().on('click', this.onClick, this);
19793         
19794         this.picker().addClass('datepicker-dropdown');
19795     
19796         this.fillTime();
19797         this.update();
19798             
19799         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19800         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19801         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19802         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19803         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19804         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19805
19806     },
19807     
19808     fireKey: function(e){
19809         if (!this.picker().isVisible()){
19810             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19811                 this.show();
19812             }
19813             return;
19814         }
19815
19816         e.preventDefault();
19817         
19818         switch(e.keyCode){
19819             case 27: // escape
19820                 this.hide();
19821                 break;
19822             case 37: // left
19823             case 39: // right
19824                 this.onTogglePeriod();
19825                 break;
19826             case 38: // up
19827                 this.onIncrementMinutes();
19828                 break;
19829             case 40: // down
19830                 this.onDecrementMinutes();
19831                 break;
19832             case 13: // enter
19833             case 9: // tab
19834                 this.setTime();
19835                 break;
19836         }
19837     },
19838     
19839     onClick: function(e) {
19840         e.stopPropagation();
19841         e.preventDefault();
19842     },
19843     
19844     picker : function()
19845     {
19846         return this.el.select('.datepicker', true).first();
19847     },
19848     
19849     fillTime: function()
19850     {    
19851         var time = this.pop.select('tbody', true).first();
19852         
19853         time.dom.innerHTML = '';
19854         
19855         time.createChild({
19856             tag: 'tr',
19857             cn: [
19858                 {
19859                     tag: 'td',
19860                     cn: [
19861                         {
19862                             tag: 'a',
19863                             href: '#',
19864                             cls: 'btn',
19865                             cn: [
19866                                 {
19867                                     tag: 'span',
19868                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19869                                 }
19870                             ]
19871                         } 
19872                     ]
19873                 },
19874                 {
19875                     tag: 'td',
19876                     cls: 'separator'
19877                 },
19878                 {
19879                     tag: 'td',
19880                     cn: [
19881                         {
19882                             tag: 'a',
19883                             href: '#',
19884                             cls: 'btn',
19885                             cn: [
19886                                 {
19887                                     tag: 'span',
19888                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19889                                 }
19890                             ]
19891                         }
19892                     ]
19893                 },
19894                 {
19895                     tag: 'td',
19896                     cls: 'separator'
19897                 }
19898             ]
19899         });
19900         
19901         time.createChild({
19902             tag: 'tr',
19903             cn: [
19904                 {
19905                     tag: 'td',
19906                     cn: [
19907                         {
19908                             tag: 'span',
19909                             cls: 'timepicker-hour',
19910                             html: '00'
19911                         }  
19912                     ]
19913                 },
19914                 {
19915                     tag: 'td',
19916                     cls: 'separator',
19917                     html: ':'
19918                 },
19919                 {
19920                     tag: 'td',
19921                     cn: [
19922                         {
19923                             tag: 'span',
19924                             cls: 'timepicker-minute',
19925                             html: '00'
19926                         }  
19927                     ]
19928                 },
19929                 {
19930                     tag: 'td',
19931                     cls: 'separator'
19932                 },
19933                 {
19934                     tag: 'td',
19935                     cn: [
19936                         {
19937                             tag: 'button',
19938                             type: 'button',
19939                             cls: 'btn btn-primary period',
19940                             html: 'AM'
19941                             
19942                         }
19943                     ]
19944                 }
19945             ]
19946         });
19947         
19948         time.createChild({
19949             tag: 'tr',
19950             cn: [
19951                 {
19952                     tag: 'td',
19953                     cn: [
19954                         {
19955                             tag: 'a',
19956                             href: '#',
19957                             cls: 'btn',
19958                             cn: [
19959                                 {
19960                                     tag: 'span',
19961                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19962                                 }
19963                             ]
19964                         }
19965                     ]
19966                 },
19967                 {
19968                     tag: 'td',
19969                     cls: 'separator'
19970                 },
19971                 {
19972                     tag: 'td',
19973                     cn: [
19974                         {
19975                             tag: 'a',
19976                             href: '#',
19977                             cls: 'btn',
19978                             cn: [
19979                                 {
19980                                     tag: 'span',
19981                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19982                                 }
19983                             ]
19984                         }
19985                     ]
19986                 },
19987                 {
19988                     tag: 'td',
19989                     cls: 'separator'
19990                 }
19991             ]
19992         });
19993         
19994     },
19995     
19996     update: function()
19997     {
19998         
19999         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20000         
20001         this.fill();
20002     },
20003     
20004     fill: function() 
20005     {
20006         var hours = this.time.getHours();
20007         var minutes = this.time.getMinutes();
20008         var period = 'AM';
20009         
20010         if(hours > 11){
20011             period = 'PM';
20012         }
20013         
20014         if(hours == 0){
20015             hours = 12;
20016         }
20017         
20018         
20019         if(hours > 12){
20020             hours = hours - 12;
20021         }
20022         
20023         if(hours < 10){
20024             hours = '0' + hours;
20025         }
20026         
20027         if(minutes < 10){
20028             minutes = '0' + minutes;
20029         }
20030         
20031         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20032         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20033         this.pop.select('button', true).first().dom.innerHTML = period;
20034         
20035     },
20036     
20037     place: function()
20038     {   
20039         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20040         
20041         var cls = ['bottom'];
20042         
20043         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20044             cls.pop();
20045             cls.push('top');
20046         }
20047         
20048         cls.push('right');
20049         
20050         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20051             cls.pop();
20052             cls.push('left');
20053         }
20054         
20055         this.picker().addClass(cls.join('-'));
20056         
20057         var _this = this;
20058         
20059         Roo.each(cls, function(c){
20060             if(c == 'bottom'){
20061                 _this.picker().setTop(_this.inputEl().getHeight());
20062                 return;
20063             }
20064             if(c == 'top'){
20065                 _this.picker().setTop(0 - _this.picker().getHeight());
20066                 return;
20067             }
20068             
20069             if(c == 'left'){
20070                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20071                 return;
20072             }
20073             if(c == 'right'){
20074                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20075                 return;
20076             }
20077         });
20078         
20079     },
20080   
20081     onFocus : function()
20082     {
20083         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20084         this.show();
20085     },
20086     
20087     onBlur : function()
20088     {
20089         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20090         this.hide();
20091     },
20092     
20093     show : function()
20094     {
20095         this.picker().show();
20096         this.pop.show();
20097         this.update();
20098         this.place();
20099         
20100         this.fireEvent('show', this, this.date);
20101     },
20102     
20103     hide : function()
20104     {
20105         this.picker().hide();
20106         this.pop.hide();
20107         
20108         this.fireEvent('hide', this, this.date);
20109     },
20110     
20111     setTime : function()
20112     {
20113         this.hide();
20114         this.setValue(this.time.format(this.format));
20115         
20116         this.fireEvent('select', this, this.date);
20117         
20118         
20119     },
20120     
20121     onMousedown: function(e){
20122         e.stopPropagation();
20123         e.preventDefault();
20124     },
20125     
20126     onIncrementHours: function()
20127     {
20128         Roo.log('onIncrementHours');
20129         this.time = this.time.add(Date.HOUR, 1);
20130         this.update();
20131         
20132     },
20133     
20134     onDecrementHours: function()
20135     {
20136         Roo.log('onDecrementHours');
20137         this.time = this.time.add(Date.HOUR, -1);
20138         this.update();
20139     },
20140     
20141     onIncrementMinutes: function()
20142     {
20143         Roo.log('onIncrementMinutes');
20144         this.time = this.time.add(Date.MINUTE, 1);
20145         this.update();
20146     },
20147     
20148     onDecrementMinutes: function()
20149     {
20150         Roo.log('onDecrementMinutes');
20151         this.time = this.time.add(Date.MINUTE, -1);
20152         this.update();
20153     },
20154     
20155     onTogglePeriod: function()
20156     {
20157         Roo.log('onTogglePeriod');
20158         this.time = this.time.add(Date.HOUR, 12);
20159         this.update();
20160     }
20161     
20162    
20163 });
20164
20165 Roo.apply(Roo.bootstrap.TimeField,  {
20166     
20167     content : {
20168         tag: 'tbody',
20169         cn: [
20170             {
20171                 tag: 'tr',
20172                 cn: [
20173                 {
20174                     tag: 'td',
20175                     colspan: '7'
20176                 }
20177                 ]
20178             }
20179         ]
20180     },
20181     
20182     footer : {
20183         tag: 'tfoot',
20184         cn: [
20185             {
20186                 tag: 'tr',
20187                 cn: [
20188                 {
20189                     tag: 'th',
20190                     colspan: '7',
20191                     cls: '',
20192                     cn: [
20193                         {
20194                             tag: 'button',
20195                             cls: 'btn btn-info ok',
20196                             html: 'OK'
20197                         }
20198                     ]
20199                 }
20200
20201                 ]
20202             }
20203         ]
20204     }
20205 });
20206
20207 Roo.apply(Roo.bootstrap.TimeField,  {
20208   
20209     template : {
20210         tag: 'div',
20211         cls: 'datepicker dropdown-menu',
20212         cn: [
20213             {
20214                 tag: 'div',
20215                 cls: 'datepicker-time',
20216                 cn: [
20217                 {
20218                     tag: 'table',
20219                     cls: 'table-condensed',
20220                     cn:[
20221                     Roo.bootstrap.TimeField.content,
20222                     Roo.bootstrap.TimeField.footer
20223                     ]
20224                 }
20225                 ]
20226             }
20227         ]
20228     }
20229 });
20230
20231  
20232
20233  /*
20234  * - LGPL
20235  *
20236  * MonthField
20237  * 
20238  */
20239
20240 /**
20241  * @class Roo.bootstrap.MonthField
20242  * @extends Roo.bootstrap.Input
20243  * Bootstrap MonthField class
20244  * 
20245  * @cfg {String} language default en
20246  * 
20247  * @constructor
20248  * Create a new MonthField
20249  * @param {Object} config The config object
20250  */
20251
20252 Roo.bootstrap.MonthField = function(config){
20253     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20254     
20255     this.addEvents({
20256         /**
20257          * @event show
20258          * Fires when this field show.
20259          * @param {Roo.bootstrap.MonthField} this
20260          * @param {Mixed} date The date value
20261          */
20262         show : true,
20263         /**
20264          * @event show
20265          * Fires when this field hide.
20266          * @param {Roo.bootstrap.MonthField} this
20267          * @param {Mixed} date The date value
20268          */
20269         hide : true,
20270         /**
20271          * @event select
20272          * Fires when select a date.
20273          * @param {Roo.bootstrap.MonthField} this
20274          * @param {String} oldvalue The old value
20275          * @param {String} newvalue The new value
20276          */
20277         select : true
20278     });
20279 };
20280
20281 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20282     
20283     onRender: function(ct, position)
20284     {
20285         
20286         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20287         
20288         this.language = this.language || 'en';
20289         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20290         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20291         
20292         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20293         this.isInline = false;
20294         this.isInput = true;
20295         this.component = this.el.select('.add-on', true).first() || false;
20296         this.component = (this.component && this.component.length === 0) ? false : this.component;
20297         this.hasInput = this.component && this.inputEL().length;
20298         
20299         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20300         
20301         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20302         
20303         this.picker().on('mousedown', this.onMousedown, this);
20304         this.picker().on('click', this.onClick, this);
20305         
20306         this.picker().addClass('datepicker-dropdown');
20307         
20308         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20309             v.setStyle('width', '189px');
20310         });
20311         
20312         this.fillMonths();
20313         
20314         this.update();
20315         
20316         if(this.isInline) {
20317             this.show();
20318         }
20319         
20320     },
20321     
20322     setValue: function(v, suppressEvent)
20323     {   
20324         var o = this.getValue();
20325         
20326         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20327         
20328         this.update();
20329
20330         if(suppressEvent !== true){
20331             this.fireEvent('select', this, o, v);
20332         }
20333         
20334     },
20335     
20336     getValue: function()
20337     {
20338         return this.value;
20339     },
20340     
20341     onClick: function(e) 
20342     {
20343         e.stopPropagation();
20344         e.preventDefault();
20345         
20346         var target = e.getTarget();
20347         
20348         if(target.nodeName.toLowerCase() === 'i'){
20349             target = Roo.get(target).dom.parentNode;
20350         }
20351         
20352         var nodeName = target.nodeName;
20353         var className = target.className;
20354         var html = target.innerHTML;
20355         
20356         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20357             return;
20358         }
20359         
20360         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20361         
20362         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20363         
20364         this.hide();
20365                         
20366     },
20367     
20368     picker : function()
20369     {
20370         return this.pickerEl;
20371     },
20372     
20373     fillMonths: function()
20374     {    
20375         var i = 0;
20376         var months = this.picker().select('>.datepicker-months td', true).first();
20377         
20378         months.dom.innerHTML = '';
20379         
20380         while (i < 12) {
20381             var month = {
20382                 tag: 'span',
20383                 cls: 'month',
20384                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20385             };
20386             
20387             months.createChild(month);
20388         }
20389         
20390     },
20391     
20392     update: function()
20393     {
20394         var _this = this;
20395         
20396         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20397             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20398         }
20399         
20400         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20401             e.removeClass('active');
20402             
20403             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20404                 e.addClass('active');
20405             }
20406         })
20407     },
20408     
20409     place: function()
20410     {
20411         if(this.isInline) {
20412             return;
20413         }
20414         
20415         this.picker().removeClass(['bottom', 'top']);
20416         
20417         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20418             /*
20419              * place to the top of element!
20420              *
20421              */
20422             
20423             this.picker().addClass('top');
20424             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20425             
20426             return;
20427         }
20428         
20429         this.picker().addClass('bottom');
20430         
20431         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20432     },
20433     
20434     onFocus : function()
20435     {
20436         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20437         this.show();
20438     },
20439     
20440     onBlur : function()
20441     {
20442         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20443         
20444         var d = this.inputEl().getValue();
20445         
20446         this.setValue(d);
20447                 
20448         this.hide();
20449     },
20450     
20451     show : function()
20452     {
20453         this.picker().show();
20454         this.picker().select('>.datepicker-months', true).first().show();
20455         this.update();
20456         this.place();
20457         
20458         this.fireEvent('show', this, this.date);
20459     },
20460     
20461     hide : function()
20462     {
20463         if(this.isInline) {
20464             return;
20465         }
20466         this.picker().hide();
20467         this.fireEvent('hide', this, this.date);
20468         
20469     },
20470     
20471     onMousedown: function(e)
20472     {
20473         e.stopPropagation();
20474         e.preventDefault();
20475     },
20476     
20477     keyup: function(e)
20478     {
20479         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20480         this.update();
20481     },
20482
20483     fireKey: function(e)
20484     {
20485         if (!this.picker().isVisible()){
20486             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20487                 this.show();
20488             }
20489             return;
20490         }
20491         
20492         var dir;
20493         
20494         switch(e.keyCode){
20495             case 27: // escape
20496                 this.hide();
20497                 e.preventDefault();
20498                 break;
20499             case 37: // left
20500             case 39: // right
20501                 dir = e.keyCode == 37 ? -1 : 1;
20502                 
20503                 this.vIndex = this.vIndex + dir;
20504                 
20505                 if(this.vIndex < 0){
20506                     this.vIndex = 0;
20507                 }
20508                 
20509                 if(this.vIndex > 11){
20510                     this.vIndex = 11;
20511                 }
20512                 
20513                 if(isNaN(this.vIndex)){
20514                     this.vIndex = 0;
20515                 }
20516                 
20517                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20518                 
20519                 break;
20520             case 38: // up
20521             case 40: // down
20522                 
20523                 dir = e.keyCode == 38 ? -1 : 1;
20524                 
20525                 this.vIndex = this.vIndex + dir * 4;
20526                 
20527                 if(this.vIndex < 0){
20528                     this.vIndex = 0;
20529                 }
20530                 
20531                 if(this.vIndex > 11){
20532                     this.vIndex = 11;
20533                 }
20534                 
20535                 if(isNaN(this.vIndex)){
20536                     this.vIndex = 0;
20537                 }
20538                 
20539                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20540                 break;
20541                 
20542             case 13: // enter
20543                 
20544                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20545                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20546                 }
20547                 
20548                 this.hide();
20549                 e.preventDefault();
20550                 break;
20551             case 9: // tab
20552                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20553                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20554                 }
20555                 this.hide();
20556                 break;
20557             case 16: // shift
20558             case 17: // ctrl
20559             case 18: // alt
20560                 break;
20561             default :
20562                 this.hide();
20563                 
20564         }
20565     },
20566     
20567     remove: function() 
20568     {
20569         this.picker().remove();
20570     }
20571    
20572 });
20573
20574 Roo.apply(Roo.bootstrap.MonthField,  {
20575     
20576     content : {
20577         tag: 'tbody',
20578         cn: [
20579         {
20580             tag: 'tr',
20581             cn: [
20582             {
20583                 tag: 'td',
20584                 colspan: '7'
20585             }
20586             ]
20587         }
20588         ]
20589     },
20590     
20591     dates:{
20592         en: {
20593             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20594             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20595         }
20596     }
20597 });
20598
20599 Roo.apply(Roo.bootstrap.MonthField,  {
20600   
20601     template : {
20602         tag: 'div',
20603         cls: 'datepicker dropdown-menu roo-dynamic',
20604         cn: [
20605             {
20606                 tag: 'div',
20607                 cls: 'datepicker-months',
20608                 cn: [
20609                 {
20610                     tag: 'table',
20611                     cls: 'table-condensed',
20612                     cn:[
20613                         Roo.bootstrap.DateField.content
20614                     ]
20615                 }
20616                 ]
20617             }
20618         ]
20619     }
20620 });
20621
20622  
20623
20624  
20625  /*
20626  * - LGPL
20627  *
20628  * CheckBox
20629  * 
20630  */
20631
20632 /**
20633  * @class Roo.bootstrap.CheckBox
20634  * @extends Roo.bootstrap.Input
20635  * Bootstrap CheckBox class
20636  * 
20637  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20638  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20639  * @cfg {String} boxLabel The text that appears beside the checkbox
20640  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20641  * @cfg {Boolean} checked initnal the element
20642  * @cfg {Boolean} inline inline the element (default false)
20643  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20644  * @cfg {String} tooltip label tooltip
20645  * 
20646  * @constructor
20647  * Create a new CheckBox
20648  * @param {Object} config The config object
20649  */
20650
20651 Roo.bootstrap.CheckBox = function(config){
20652     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20653    
20654     this.addEvents({
20655         /**
20656         * @event check
20657         * Fires when the element is checked or unchecked.
20658         * @param {Roo.bootstrap.CheckBox} this This input
20659         * @param {Boolean} checked The new checked value
20660         */
20661        check : true,
20662        /**
20663         * @event click
20664         * Fires when the element is click.
20665         * @param {Roo.bootstrap.CheckBox} this This input
20666         */
20667        click : true
20668     });
20669     
20670 };
20671
20672 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20673   
20674     inputType: 'checkbox',
20675     inputValue: 1,
20676     valueOff: 0,
20677     boxLabel: false,
20678     checked: false,
20679     weight : false,
20680     inline: false,
20681     tooltip : '',
20682     
20683     getAutoCreate : function()
20684     {
20685         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20686         
20687         var id = Roo.id();
20688         
20689         var cfg = {};
20690         
20691         cfg.cls = 'form-group ' + this.inputType; //input-group
20692         
20693         if(this.inline){
20694             cfg.cls += ' ' + this.inputType + '-inline';
20695         }
20696         
20697         var input =  {
20698             tag: 'input',
20699             id : id,
20700             type : this.inputType,
20701             value : this.inputValue,
20702             cls : 'roo-' + this.inputType, //'form-box',
20703             placeholder : this.placeholder || ''
20704             
20705         };
20706         
20707         if(this.inputType != 'radio'){
20708             var hidden =  {
20709                 tag: 'input',
20710                 type : 'hidden',
20711                 cls : 'roo-hidden-value',
20712                 value : this.checked ? this.inputValue : this.valueOff
20713             };
20714         }
20715         
20716             
20717         if (this.weight) { // Validity check?
20718             cfg.cls += " " + this.inputType + "-" + this.weight;
20719         }
20720         
20721         if (this.disabled) {
20722             input.disabled=true;
20723         }
20724         
20725         if(this.checked){
20726             input.checked = this.checked;
20727         }
20728         
20729         if (this.name) {
20730             
20731             input.name = this.name;
20732             
20733             if(this.inputType != 'radio'){
20734                 hidden.name = this.name;
20735                 input.name = '_hidden_' + this.name;
20736             }
20737         }
20738         
20739         if (this.size) {
20740             input.cls += ' input-' + this.size;
20741         }
20742         
20743         var settings=this;
20744         
20745         ['xs','sm','md','lg'].map(function(size){
20746             if (settings[size]) {
20747                 cfg.cls += ' col-' + size + '-' + settings[size];
20748             }
20749         });
20750         
20751         var inputblock = input;
20752          
20753         if (this.before || this.after) {
20754             
20755             inputblock = {
20756                 cls : 'input-group',
20757                 cn :  [] 
20758             };
20759             
20760             if (this.before) {
20761                 inputblock.cn.push({
20762                     tag :'span',
20763                     cls : 'input-group-addon',
20764                     html : this.before
20765                 });
20766             }
20767             
20768             inputblock.cn.push(input);
20769             
20770             if(this.inputType != 'radio'){
20771                 inputblock.cn.push(hidden);
20772             }
20773             
20774             if (this.after) {
20775                 inputblock.cn.push({
20776                     tag :'span',
20777                     cls : 'input-group-addon',
20778                     html : this.after
20779                 });
20780             }
20781             
20782         }
20783         
20784         if (align ==='left' && this.fieldLabel.length) {
20785 //                Roo.log("left and has label");
20786             cfg.cn = [
20787                 {
20788                     tag: 'label',
20789                     'for' :  id,
20790                     cls : 'control-label',
20791                     html : this.fieldLabel
20792                 },
20793                 {
20794                     cls : "", 
20795                     cn: [
20796                         inputblock
20797                     ]
20798                 }
20799             ];
20800             
20801             if(this.labelWidth > 12){
20802                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20803             }
20804             
20805             if(this.labelWidth < 13 && this.labelmd == 0){
20806                 this.labelmd = this.labelWidth;
20807             }
20808             
20809             if(this.labellg > 0){
20810                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20811                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20812             }
20813             
20814             if(this.labelmd > 0){
20815                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20816                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20817             }
20818             
20819             if(this.labelsm > 0){
20820                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20821                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20822             }
20823             
20824             if(this.labelxs > 0){
20825                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20826                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20827             }
20828             
20829         } else if ( this.fieldLabel.length) {
20830 //                Roo.log(" label");
20831                 cfg.cn = [
20832                    
20833                     {
20834                         tag: this.boxLabel ? 'span' : 'label',
20835                         'for': id,
20836                         cls: 'control-label box-input-label',
20837                         //cls : 'input-group-addon',
20838                         html : this.fieldLabel
20839                     },
20840                     
20841                     inputblock
20842                     
20843                 ];
20844
20845         } else {
20846             
20847 //                Roo.log(" no label && no align");
20848                 cfg.cn = [  inputblock ] ;
20849                 
20850                 
20851         }
20852         
20853         if(this.boxLabel){
20854              var boxLabelCfg = {
20855                 tag: 'label',
20856                 //'for': id, // box label is handled by onclick - so no for...
20857                 cls: 'box-label',
20858                 html: this.boxLabel
20859             };
20860             
20861             if(this.tooltip){
20862                 boxLabelCfg.tooltip = this.tooltip;
20863             }
20864              
20865             cfg.cn.push(boxLabelCfg);
20866         }
20867         
20868         if(this.inputType != 'radio'){
20869             cfg.cn.push(hidden);
20870         }
20871         
20872         return cfg;
20873         
20874     },
20875     
20876     /**
20877      * return the real input element.
20878      */
20879     inputEl: function ()
20880     {
20881         return this.el.select('input.roo-' + this.inputType,true).first();
20882     },
20883     hiddenEl: function ()
20884     {
20885         return this.el.select('input.roo-hidden-value',true).first();
20886     },
20887     
20888     labelEl: function()
20889     {
20890         return this.el.select('label.control-label',true).first();
20891     },
20892     /* depricated... */
20893     
20894     label: function()
20895     {
20896         return this.labelEl();
20897     },
20898     
20899     boxLabelEl: function()
20900     {
20901         return this.el.select('label.box-label',true).first();
20902     },
20903     
20904     initEvents : function()
20905     {
20906 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20907         
20908         this.inputEl().on('click', this.onClick,  this);
20909         
20910         if (this.boxLabel) { 
20911             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20912         }
20913         
20914         this.startValue = this.getValue();
20915         
20916         if(this.groupId){
20917             Roo.bootstrap.CheckBox.register(this);
20918         }
20919     },
20920     
20921     onClick : function(e)
20922     {   
20923         if(this.fireEvent('click', this, e) !== false){
20924             this.setChecked(!this.checked);
20925         }
20926         
20927     },
20928     
20929     setChecked : function(state,suppressEvent)
20930     {
20931         this.startValue = this.getValue();
20932
20933         if(this.inputType == 'radio'){
20934             
20935             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20936                 e.dom.checked = false;
20937             });
20938             
20939             this.inputEl().dom.checked = true;
20940             
20941             this.inputEl().dom.value = this.inputValue;
20942             
20943             if(suppressEvent !== true){
20944                 this.fireEvent('check', this, true);
20945             }
20946             
20947             this.validate();
20948             
20949             return;
20950         }
20951         
20952         this.checked = state;
20953         
20954         this.inputEl().dom.checked = state;
20955         
20956         
20957         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20958         
20959         if(suppressEvent !== true){
20960             this.fireEvent('check', this, state);
20961         }
20962         
20963         this.validate();
20964     },
20965     
20966     getValue : function()
20967     {
20968         if(this.inputType == 'radio'){
20969             return this.getGroupValue();
20970         }
20971         
20972         return this.hiddenEl().dom.value;
20973         
20974     },
20975     
20976     getGroupValue : function()
20977     {
20978         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20979             return '';
20980         }
20981         
20982         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20983     },
20984     
20985     setValue : function(v,suppressEvent)
20986     {
20987         if(this.inputType == 'radio'){
20988             this.setGroupValue(v, suppressEvent);
20989             return;
20990         }
20991         
20992         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20993         
20994         this.validate();
20995     },
20996     
20997     setGroupValue : function(v, suppressEvent)
20998     {
20999         this.startValue = this.getValue();
21000         
21001         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21002             e.dom.checked = false;
21003             
21004             if(e.dom.value == v){
21005                 e.dom.checked = true;
21006             }
21007         });
21008         
21009         if(suppressEvent !== true){
21010             this.fireEvent('check', this, true);
21011         }
21012
21013         this.validate();
21014         
21015         return;
21016     },
21017     
21018     validate : function()
21019     {
21020         if(this.getVisibilityEl().hasClass('hidden')){
21021             return true;
21022         }
21023         
21024         if(
21025                 this.disabled || 
21026                 (this.inputType == 'radio' && this.validateRadio()) ||
21027                 (this.inputType == 'checkbox' && this.validateCheckbox())
21028         ){
21029             this.markValid();
21030             return true;
21031         }
21032         
21033         this.markInvalid();
21034         return false;
21035     },
21036     
21037     validateRadio : function()
21038     {
21039         if(this.getVisibilityEl().hasClass('hidden')){
21040             return true;
21041         }
21042         
21043         if(this.allowBlank){
21044             return true;
21045         }
21046         
21047         var valid = false;
21048         
21049         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21050             if(!e.dom.checked){
21051                 return;
21052             }
21053             
21054             valid = true;
21055             
21056             return false;
21057         });
21058         
21059         return valid;
21060     },
21061     
21062     validateCheckbox : function()
21063     {
21064         if(!this.groupId){
21065             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21066             //return (this.getValue() == this.inputValue) ? true : false;
21067         }
21068         
21069         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21070         
21071         if(!group){
21072             return false;
21073         }
21074         
21075         var r = false;
21076         
21077         for(var i in group){
21078             if(group[i].el.isVisible(true)){
21079                 r = false;
21080                 break;
21081             }
21082             
21083             r = true;
21084         }
21085         
21086         for(var i in group){
21087             if(r){
21088                 break;
21089             }
21090             
21091             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21092         }
21093         
21094         return r;
21095     },
21096     
21097     /**
21098      * Mark this field as valid
21099      */
21100     markValid : function()
21101     {
21102         var _this = this;
21103         
21104         this.fireEvent('valid', this);
21105         
21106         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21107         
21108         if(this.groupId){
21109             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21110         }
21111         
21112         if(label){
21113             label.markValid();
21114         }
21115
21116         if(this.inputType == 'radio'){
21117             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21118                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21119                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21120             });
21121             
21122             return;
21123         }
21124
21125         if(!this.groupId){
21126             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21127             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21128             return;
21129         }
21130         
21131         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21132         
21133         if(!group){
21134             return;
21135         }
21136         
21137         for(var i in group){
21138             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21139             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21140         }
21141     },
21142     
21143      /**
21144      * Mark this field as invalid
21145      * @param {String} msg The validation message
21146      */
21147     markInvalid : function(msg)
21148     {
21149         if(this.allowBlank){
21150             return;
21151         }
21152         
21153         var _this = this;
21154         
21155         this.fireEvent('invalid', this, msg);
21156         
21157         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21158         
21159         if(this.groupId){
21160             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21161         }
21162         
21163         if(label){
21164             label.markInvalid();
21165         }
21166             
21167         if(this.inputType == 'radio'){
21168             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21169                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21170                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21171             });
21172             
21173             return;
21174         }
21175         
21176         if(!this.groupId){
21177             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21178             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21179             return;
21180         }
21181         
21182         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21183         
21184         if(!group){
21185             return;
21186         }
21187         
21188         for(var i in group){
21189             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21190             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21191         }
21192         
21193     },
21194     
21195     clearInvalid : function()
21196     {
21197         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21198         
21199         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21200         
21201         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21202         
21203         if (label && label.iconEl) {
21204             label.iconEl.removeClass(label.validClass);
21205             label.iconEl.removeClass(label.invalidClass);
21206         }
21207     },
21208     
21209     disable : function()
21210     {
21211         if(this.inputType != 'radio'){
21212             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21213             return;
21214         }
21215         
21216         var _this = this;
21217         
21218         if(this.rendered){
21219             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21220                 _this.getActionEl().addClass(this.disabledClass);
21221                 e.dom.disabled = true;
21222             });
21223         }
21224         
21225         this.disabled = true;
21226         this.fireEvent("disable", this);
21227         return this;
21228     },
21229
21230     enable : function()
21231     {
21232         if(this.inputType != 'radio'){
21233             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21234             return;
21235         }
21236         
21237         var _this = this;
21238         
21239         if(this.rendered){
21240             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21241                 _this.getActionEl().removeClass(this.disabledClass);
21242                 e.dom.disabled = false;
21243             });
21244         }
21245         
21246         this.disabled = false;
21247         this.fireEvent("enable", this);
21248         return this;
21249     },
21250     
21251     setBoxLabel : function(v)
21252     {
21253         this.boxLabel = v;
21254         
21255         if(this.rendered){
21256             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21257         }
21258     }
21259
21260 });
21261
21262 Roo.apply(Roo.bootstrap.CheckBox, {
21263     
21264     groups: {},
21265     
21266      /**
21267     * register a CheckBox Group
21268     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21269     */
21270     register : function(checkbox)
21271     {
21272         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21273             this.groups[checkbox.groupId] = {};
21274         }
21275         
21276         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21277             return;
21278         }
21279         
21280         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21281         
21282     },
21283     /**
21284     * fetch a CheckBox Group based on the group ID
21285     * @param {string} the group ID
21286     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21287     */
21288     get: function(groupId) {
21289         if (typeof(this.groups[groupId]) == 'undefined') {
21290             return false;
21291         }
21292         
21293         return this.groups[groupId] ;
21294     }
21295     
21296     
21297 });
21298 /*
21299  * - LGPL
21300  *
21301  * RadioItem
21302  * 
21303  */
21304
21305 /**
21306  * @class Roo.bootstrap.Radio
21307  * @extends Roo.bootstrap.Component
21308  * Bootstrap Radio class
21309  * @cfg {String} boxLabel - the label associated
21310  * @cfg {String} value - the value of radio
21311  * 
21312  * @constructor
21313  * Create a new Radio
21314  * @param {Object} config The config object
21315  */
21316 Roo.bootstrap.Radio = function(config){
21317     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21318     
21319 };
21320
21321 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21322     
21323     boxLabel : '',
21324     
21325     value : '',
21326     
21327     getAutoCreate : function()
21328     {
21329         var cfg = {
21330             tag : 'div',
21331             cls : 'form-group radio',
21332             cn : [
21333                 {
21334                     tag : 'label',
21335                     cls : 'box-label',
21336                     html : this.boxLabel
21337                 }
21338             ]
21339         };
21340         
21341         return cfg;
21342     },
21343     
21344     initEvents : function() 
21345     {
21346         this.parent().register(this);
21347         
21348         this.el.on('click', this.onClick, this);
21349         
21350     },
21351     
21352     onClick : function(e)
21353     {
21354         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21355             this.setChecked(true);
21356         }
21357     },
21358     
21359     setChecked : function(state, suppressEvent)
21360     {
21361         this.parent().setValue(this.value, suppressEvent);
21362         
21363     },
21364     
21365     setBoxLabel : function(v)
21366     {
21367         this.boxLabel = v;
21368         
21369         if(this.rendered){
21370             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21371         }
21372     }
21373     
21374 });
21375  
21376
21377  /*
21378  * - LGPL
21379  *
21380  * Input
21381  * 
21382  */
21383
21384 /**
21385  * @class Roo.bootstrap.SecurePass
21386  * @extends Roo.bootstrap.Input
21387  * Bootstrap SecurePass class
21388  *
21389  * 
21390  * @constructor
21391  * Create a new SecurePass
21392  * @param {Object} config The config object
21393  */
21394  
21395 Roo.bootstrap.SecurePass = function (config) {
21396     // these go here, so the translation tool can replace them..
21397     this.errors = {
21398         PwdEmpty: "Please type a password, and then retype it to confirm.",
21399         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21400         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21401         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21402         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21403         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21404         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21405         TooWeak: "Your password is Too Weak."
21406     },
21407     this.meterLabel = "Password strength:";
21408     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21409     this.meterClass = [
21410         "roo-password-meter-tooweak", 
21411         "roo-password-meter-weak", 
21412         "roo-password-meter-medium", 
21413         "roo-password-meter-strong", 
21414         "roo-password-meter-grey"
21415     ];
21416     
21417     this.errors = {};
21418     
21419     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21420 }
21421
21422 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21423     /**
21424      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21425      * {
21426      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21427      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21428      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21429      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21430      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21431      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21432      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21433      * })
21434      */
21435     // private
21436     
21437     meterWidth: 300,
21438     errorMsg :'',    
21439     errors: false,
21440     imageRoot: '/',
21441     /**
21442      * @cfg {String/Object} Label for the strength meter (defaults to
21443      * 'Password strength:')
21444      */
21445     // private
21446     meterLabel: '',
21447     /**
21448      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21449      * ['Weak', 'Medium', 'Strong'])
21450      */
21451     // private    
21452     pwdStrengths: false,    
21453     // private
21454     strength: 0,
21455     // private
21456     _lastPwd: null,
21457     // private
21458     kCapitalLetter: 0,
21459     kSmallLetter: 1,
21460     kDigit: 2,
21461     kPunctuation: 3,
21462     
21463     insecure: false,
21464     // private
21465     initEvents: function ()
21466     {
21467         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21468
21469         if (this.el.is('input[type=password]') && Roo.isSafari) {
21470             this.el.on('keydown', this.SafariOnKeyDown, this);
21471         }
21472
21473         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21474     },
21475     // private
21476     onRender: function (ct, position)
21477     {
21478         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21479         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21480         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21481
21482         this.trigger.createChild({
21483                    cn: [
21484                     {
21485                     //id: 'PwdMeter',
21486                     tag: 'div',
21487                     cls: 'roo-password-meter-grey col-xs-12',
21488                     style: {
21489                         //width: 0,
21490                         //width: this.meterWidth + 'px'                                                
21491                         }
21492                     },
21493                     {                            
21494                          cls: 'roo-password-meter-text'                          
21495                     }
21496                 ]            
21497         });
21498
21499          
21500         if (this.hideTrigger) {
21501             this.trigger.setDisplayed(false);
21502         }
21503         this.setSize(this.width || '', this.height || '');
21504     },
21505     // private
21506     onDestroy: function ()
21507     {
21508         if (this.trigger) {
21509             this.trigger.removeAllListeners();
21510             this.trigger.remove();
21511         }
21512         if (this.wrap) {
21513             this.wrap.remove();
21514         }
21515         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21516     },
21517     // private
21518     checkStrength: function ()
21519     {
21520         var pwd = this.inputEl().getValue();
21521         if (pwd == this._lastPwd) {
21522             return;
21523         }
21524
21525         var strength;
21526         if (this.ClientSideStrongPassword(pwd)) {
21527             strength = 3;
21528         } else if (this.ClientSideMediumPassword(pwd)) {
21529             strength = 2;
21530         } else if (this.ClientSideWeakPassword(pwd)) {
21531             strength = 1;
21532         } else {
21533             strength = 0;
21534         }
21535         
21536         Roo.log('strength1: ' + strength);
21537         
21538         //var pm = this.trigger.child('div/div/div').dom;
21539         var pm = this.trigger.child('div/div');
21540         pm.removeClass(this.meterClass);
21541         pm.addClass(this.meterClass[strength]);
21542                 
21543         
21544         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21545                 
21546         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21547         
21548         this._lastPwd = pwd;
21549     },
21550     reset: function ()
21551     {
21552         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21553         
21554         this._lastPwd = '';
21555         
21556         var pm = this.trigger.child('div/div');
21557         pm.removeClass(this.meterClass);
21558         pm.addClass('roo-password-meter-grey');        
21559         
21560         
21561         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21562         
21563         pt.innerHTML = '';
21564         this.inputEl().dom.type='password';
21565     },
21566     // private
21567     validateValue: function (value)
21568     {
21569         
21570         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21571             return false;
21572         }
21573         if (value.length == 0) {
21574             if (this.allowBlank) {
21575                 this.clearInvalid();
21576                 return true;
21577             }
21578
21579             this.markInvalid(this.errors.PwdEmpty);
21580             this.errorMsg = this.errors.PwdEmpty;
21581             return false;
21582         }
21583         
21584         if(this.insecure){
21585             return true;
21586         }
21587         
21588         if ('[\x21-\x7e]*'.match(value)) {
21589             this.markInvalid(this.errors.PwdBadChar);
21590             this.errorMsg = this.errors.PwdBadChar;
21591             return false;
21592         }
21593         if (value.length < 6) {
21594             this.markInvalid(this.errors.PwdShort);
21595             this.errorMsg = this.errors.PwdShort;
21596             return false;
21597         }
21598         if (value.length > 16) {
21599             this.markInvalid(this.errors.PwdLong);
21600             this.errorMsg = this.errors.PwdLong;
21601             return false;
21602         }
21603         var strength;
21604         if (this.ClientSideStrongPassword(value)) {
21605             strength = 3;
21606         } else if (this.ClientSideMediumPassword(value)) {
21607             strength = 2;
21608         } else if (this.ClientSideWeakPassword(value)) {
21609             strength = 1;
21610         } else {
21611             strength = 0;
21612         }
21613
21614         
21615         if (strength < 2) {
21616             //this.markInvalid(this.errors.TooWeak);
21617             this.errorMsg = this.errors.TooWeak;
21618             //return false;
21619         }
21620         
21621         
21622         console.log('strength2: ' + strength);
21623         
21624         //var pm = this.trigger.child('div/div/div').dom;
21625         
21626         var pm = this.trigger.child('div/div');
21627         pm.removeClass(this.meterClass);
21628         pm.addClass(this.meterClass[strength]);
21629                 
21630         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21631                 
21632         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21633         
21634         this.errorMsg = ''; 
21635         return true;
21636     },
21637     // private
21638     CharacterSetChecks: function (type)
21639     {
21640         this.type = type;
21641         this.fResult = false;
21642     },
21643     // private
21644     isctype: function (character, type)
21645     {
21646         switch (type) {  
21647             case this.kCapitalLetter:
21648                 if (character >= 'A' && character <= 'Z') {
21649                     return true;
21650                 }
21651                 break;
21652             
21653             case this.kSmallLetter:
21654                 if (character >= 'a' && character <= 'z') {
21655                     return true;
21656                 }
21657                 break;
21658             
21659             case this.kDigit:
21660                 if (character >= '0' && character <= '9') {
21661                     return true;
21662                 }
21663                 break;
21664             
21665             case this.kPunctuation:
21666                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21667                     return true;
21668                 }
21669                 break;
21670             
21671             default:
21672                 return false;
21673         }
21674
21675     },
21676     // private
21677     IsLongEnough: function (pwd, size)
21678     {
21679         return !(pwd == null || isNaN(size) || pwd.length < size);
21680     },
21681     // private
21682     SpansEnoughCharacterSets: function (word, nb)
21683     {
21684         if (!this.IsLongEnough(word, nb))
21685         {
21686             return false;
21687         }
21688
21689         var characterSetChecks = new Array(
21690             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21691             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21692         );
21693         
21694         for (var index = 0; index < word.length; ++index) {
21695             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21696                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21697                     characterSetChecks[nCharSet].fResult = true;
21698                     break;
21699                 }
21700             }
21701         }
21702
21703         var nCharSets = 0;
21704         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21705             if (characterSetChecks[nCharSet].fResult) {
21706                 ++nCharSets;
21707             }
21708         }
21709
21710         if (nCharSets < nb) {
21711             return false;
21712         }
21713         return true;
21714     },
21715     // private
21716     ClientSideStrongPassword: function (pwd)
21717     {
21718         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21719     },
21720     // private
21721     ClientSideMediumPassword: function (pwd)
21722     {
21723         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21724     },
21725     // private
21726     ClientSideWeakPassword: function (pwd)
21727     {
21728         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21729     }
21730           
21731 })//<script type="text/javascript">
21732
21733 /*
21734  * Based  Ext JS Library 1.1.1
21735  * Copyright(c) 2006-2007, Ext JS, LLC.
21736  * LGPL
21737  *
21738  */
21739  
21740 /**
21741  * @class Roo.HtmlEditorCore
21742  * @extends Roo.Component
21743  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21744  *
21745  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21746  */
21747
21748 Roo.HtmlEditorCore = function(config){
21749     
21750     
21751     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21752     
21753     
21754     this.addEvents({
21755         /**
21756          * @event initialize
21757          * Fires when the editor is fully initialized (including the iframe)
21758          * @param {Roo.HtmlEditorCore} this
21759          */
21760         initialize: true,
21761         /**
21762          * @event activate
21763          * Fires when the editor is first receives the focus. Any insertion must wait
21764          * until after this event.
21765          * @param {Roo.HtmlEditorCore} this
21766          */
21767         activate: true,
21768          /**
21769          * @event beforesync
21770          * Fires before the textarea is updated with content from the editor iframe. Return false
21771          * to cancel the sync.
21772          * @param {Roo.HtmlEditorCore} this
21773          * @param {String} html
21774          */
21775         beforesync: true,
21776          /**
21777          * @event beforepush
21778          * Fires before the iframe editor is updated with content from the textarea. Return false
21779          * to cancel the push.
21780          * @param {Roo.HtmlEditorCore} this
21781          * @param {String} html
21782          */
21783         beforepush: true,
21784          /**
21785          * @event sync
21786          * Fires when the textarea is updated with content from the editor iframe.
21787          * @param {Roo.HtmlEditorCore} this
21788          * @param {String} html
21789          */
21790         sync: true,
21791          /**
21792          * @event push
21793          * Fires when the iframe editor is updated with content from the textarea.
21794          * @param {Roo.HtmlEditorCore} this
21795          * @param {String} html
21796          */
21797         push: true,
21798         
21799         /**
21800          * @event editorevent
21801          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21802          * @param {Roo.HtmlEditorCore} this
21803          */
21804         editorevent: true
21805         
21806     });
21807     
21808     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21809     
21810     // defaults : white / black...
21811     this.applyBlacklists();
21812     
21813     
21814     
21815 };
21816
21817
21818 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21819
21820
21821      /**
21822      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21823      */
21824     
21825     owner : false,
21826     
21827      /**
21828      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21829      *                        Roo.resizable.
21830      */
21831     resizable : false,
21832      /**
21833      * @cfg {Number} height (in pixels)
21834      */   
21835     height: 300,
21836    /**
21837      * @cfg {Number} width (in pixels)
21838      */   
21839     width: 500,
21840     
21841     /**
21842      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21843      * 
21844      */
21845     stylesheets: false,
21846     
21847     // id of frame..
21848     frameId: false,
21849     
21850     // private properties
21851     validationEvent : false,
21852     deferHeight: true,
21853     initialized : false,
21854     activated : false,
21855     sourceEditMode : false,
21856     onFocus : Roo.emptyFn,
21857     iframePad:3,
21858     hideMode:'offsets',
21859     
21860     clearUp: true,
21861     
21862     // blacklist + whitelisted elements..
21863     black: false,
21864     white: false,
21865      
21866     bodyCls : '',
21867
21868     /**
21869      * Protected method that will not generally be called directly. It
21870      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21871      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21872      */
21873     getDocMarkup : function(){
21874         // body styles..
21875         var st = '';
21876         
21877         // inherit styels from page...?? 
21878         if (this.stylesheets === false) {
21879             
21880             Roo.get(document.head).select('style').each(function(node) {
21881                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21882             });
21883             
21884             Roo.get(document.head).select('link').each(function(node) { 
21885                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21886             });
21887             
21888         } else if (!this.stylesheets.length) {
21889                 // simple..
21890                 st = '<style type="text/css">' +
21891                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21892                    '</style>';
21893         } else { 
21894             st = '<style type="text/css">' +
21895                     this.stylesheets +
21896                 '</style>';
21897         }
21898         
21899         st +=  '<style type="text/css">' +
21900             'IMG { cursor: pointer } ' +
21901         '</style>';
21902
21903         var cls = 'roo-htmleditor-body';
21904         
21905         if(this.bodyCls.length){
21906             cls += ' ' + this.bodyCls;
21907         }
21908         
21909         return '<html><head>' + st  +
21910             //<style type="text/css">' +
21911             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21912             //'</style>' +
21913             ' </head><body class="' +  cls + '"></body></html>';
21914     },
21915
21916     // private
21917     onRender : function(ct, position)
21918     {
21919         var _t = this;
21920         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21921         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21922         
21923         
21924         this.el.dom.style.border = '0 none';
21925         this.el.dom.setAttribute('tabIndex', -1);
21926         this.el.addClass('x-hidden hide');
21927         
21928         
21929         
21930         if(Roo.isIE){ // fix IE 1px bogus margin
21931             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21932         }
21933        
21934         
21935         this.frameId = Roo.id();
21936         
21937          
21938         
21939         var iframe = this.owner.wrap.createChild({
21940             tag: 'iframe',
21941             cls: 'form-control', // bootstrap..
21942             id: this.frameId,
21943             name: this.frameId,
21944             frameBorder : 'no',
21945             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21946         }, this.el
21947         );
21948         
21949         
21950         this.iframe = iframe.dom;
21951
21952          this.assignDocWin();
21953         
21954         this.doc.designMode = 'on';
21955        
21956         this.doc.open();
21957         this.doc.write(this.getDocMarkup());
21958         this.doc.close();
21959
21960         
21961         var task = { // must defer to wait for browser to be ready
21962             run : function(){
21963                 //console.log("run task?" + this.doc.readyState);
21964                 this.assignDocWin();
21965                 if(this.doc.body || this.doc.readyState == 'complete'){
21966                     try {
21967                         this.doc.designMode="on";
21968                     } catch (e) {
21969                         return;
21970                     }
21971                     Roo.TaskMgr.stop(task);
21972                     this.initEditor.defer(10, this);
21973                 }
21974             },
21975             interval : 10,
21976             duration: 10000,
21977             scope: this
21978         };
21979         Roo.TaskMgr.start(task);
21980
21981     },
21982
21983     // private
21984     onResize : function(w, h)
21985     {
21986          Roo.log('resize: ' +w + ',' + h );
21987         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21988         if(!this.iframe){
21989             return;
21990         }
21991         if(typeof w == 'number'){
21992             
21993             this.iframe.style.width = w + 'px';
21994         }
21995         if(typeof h == 'number'){
21996             
21997             this.iframe.style.height = h + 'px';
21998             if(this.doc){
21999                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22000             }
22001         }
22002         
22003     },
22004
22005     /**
22006      * Toggles the editor between standard and source edit mode.
22007      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22008      */
22009     toggleSourceEdit : function(sourceEditMode){
22010         
22011         this.sourceEditMode = sourceEditMode === true;
22012         
22013         if(this.sourceEditMode){
22014  
22015             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22016             
22017         }else{
22018             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22019             //this.iframe.className = '';
22020             this.deferFocus();
22021         }
22022         //this.setSize(this.owner.wrap.getSize());
22023         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22024     },
22025
22026     
22027   
22028
22029     /**
22030      * Protected method that will not generally be called directly. If you need/want
22031      * custom HTML cleanup, this is the method you should override.
22032      * @param {String} html The HTML to be cleaned
22033      * return {String} The cleaned HTML
22034      */
22035     cleanHtml : function(html){
22036         html = String(html);
22037         if(html.length > 5){
22038             if(Roo.isSafari){ // strip safari nonsense
22039                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22040             }
22041         }
22042         if(html == '&nbsp;'){
22043             html = '';
22044         }
22045         return html;
22046     },
22047
22048     /**
22049      * HTML Editor -> Textarea
22050      * Protected method that will not generally be called directly. Syncs the contents
22051      * of the editor iframe with the textarea.
22052      */
22053     syncValue : function(){
22054         if(this.initialized){
22055             var bd = (this.doc.body || this.doc.documentElement);
22056             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22057             var html = bd.innerHTML;
22058             if(Roo.isSafari){
22059                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22060                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22061                 if(m && m[1]){
22062                     html = '<div style="'+m[0]+'">' + html + '</div>';
22063                 }
22064             }
22065             html = this.cleanHtml(html);
22066             // fix up the special chars.. normaly like back quotes in word...
22067             // however we do not want to do this with chinese..
22068             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22069                 var cc = b.charCodeAt();
22070                 if (
22071                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22072                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22073                     (cc >= 0xf900 && cc < 0xfb00 )
22074                 ) {
22075                         return b;
22076                 }
22077                 return "&#"+cc+";" 
22078             });
22079             if(this.owner.fireEvent('beforesync', this, html) !== false){
22080                 this.el.dom.value = html;
22081                 this.owner.fireEvent('sync', this, html);
22082             }
22083         }
22084     },
22085
22086     /**
22087      * Protected method that will not generally be called directly. Pushes the value of the textarea
22088      * into the iframe editor.
22089      */
22090     pushValue : function(){
22091         if(this.initialized){
22092             var v = this.el.dom.value.trim();
22093             
22094 //            if(v.length < 1){
22095 //                v = '&#160;';
22096 //            }
22097             
22098             if(this.owner.fireEvent('beforepush', this, v) !== false){
22099                 var d = (this.doc.body || this.doc.documentElement);
22100                 d.innerHTML = v;
22101                 this.cleanUpPaste();
22102                 this.el.dom.value = d.innerHTML;
22103                 this.owner.fireEvent('push', this, v);
22104             }
22105         }
22106     },
22107
22108     // private
22109     deferFocus : function(){
22110         this.focus.defer(10, this);
22111     },
22112
22113     // doc'ed in Field
22114     focus : function(){
22115         if(this.win && !this.sourceEditMode){
22116             this.win.focus();
22117         }else{
22118             this.el.focus();
22119         }
22120     },
22121     
22122     assignDocWin: function()
22123     {
22124         var iframe = this.iframe;
22125         
22126          if(Roo.isIE){
22127             this.doc = iframe.contentWindow.document;
22128             this.win = iframe.contentWindow;
22129         } else {
22130 //            if (!Roo.get(this.frameId)) {
22131 //                return;
22132 //            }
22133 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22134 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22135             
22136             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22137                 return;
22138             }
22139             
22140             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22141             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22142         }
22143     },
22144     
22145     // private
22146     initEditor : function(){
22147         //console.log("INIT EDITOR");
22148         this.assignDocWin();
22149         
22150         
22151         
22152         this.doc.designMode="on";
22153         this.doc.open();
22154         this.doc.write(this.getDocMarkup());
22155         this.doc.close();
22156         
22157         var dbody = (this.doc.body || this.doc.documentElement);
22158         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22159         // this copies styles from the containing element into thsi one..
22160         // not sure why we need all of this..
22161         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22162         
22163         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22164         //ss['background-attachment'] = 'fixed'; // w3c
22165         dbody.bgProperties = 'fixed'; // ie
22166         //Roo.DomHelper.applyStyles(dbody, ss);
22167         Roo.EventManager.on(this.doc, {
22168             //'mousedown': this.onEditorEvent,
22169             'mouseup': this.onEditorEvent,
22170             'dblclick': this.onEditorEvent,
22171             'click': this.onEditorEvent,
22172             'keyup': this.onEditorEvent,
22173             buffer:100,
22174             scope: this
22175         });
22176         if(Roo.isGecko){
22177             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22178         }
22179         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22180             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22181         }
22182         this.initialized = true;
22183
22184         this.owner.fireEvent('initialize', this);
22185         this.pushValue();
22186     },
22187
22188     // private
22189     onDestroy : function(){
22190         
22191         
22192         
22193         if(this.rendered){
22194             
22195             //for (var i =0; i < this.toolbars.length;i++) {
22196             //    // fixme - ask toolbars for heights?
22197             //    this.toolbars[i].onDestroy();
22198            // }
22199             
22200             //this.wrap.dom.innerHTML = '';
22201             //this.wrap.remove();
22202         }
22203     },
22204
22205     // private
22206     onFirstFocus : function(){
22207         
22208         this.assignDocWin();
22209         
22210         
22211         this.activated = true;
22212          
22213     
22214         if(Roo.isGecko){ // prevent silly gecko errors
22215             this.win.focus();
22216             var s = this.win.getSelection();
22217             if(!s.focusNode || s.focusNode.nodeType != 3){
22218                 var r = s.getRangeAt(0);
22219                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22220                 r.collapse(true);
22221                 this.deferFocus();
22222             }
22223             try{
22224                 this.execCmd('useCSS', true);
22225                 this.execCmd('styleWithCSS', false);
22226             }catch(e){}
22227         }
22228         this.owner.fireEvent('activate', this);
22229     },
22230
22231     // private
22232     adjustFont: function(btn){
22233         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22234         //if(Roo.isSafari){ // safari
22235         //    adjust *= 2;
22236        // }
22237         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22238         if(Roo.isSafari){ // safari
22239             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22240             v =  (v < 10) ? 10 : v;
22241             v =  (v > 48) ? 48 : v;
22242             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22243             
22244         }
22245         
22246         
22247         v = Math.max(1, v+adjust);
22248         
22249         this.execCmd('FontSize', v  );
22250     },
22251
22252     onEditorEvent : function(e)
22253     {
22254         this.owner.fireEvent('editorevent', this, e);
22255       //  this.updateToolbar();
22256         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22257     },
22258
22259     insertTag : function(tg)
22260     {
22261         // could be a bit smarter... -> wrap the current selected tRoo..
22262         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22263             
22264             range = this.createRange(this.getSelection());
22265             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22266             wrappingNode.appendChild(range.extractContents());
22267             range.insertNode(wrappingNode);
22268
22269             return;
22270             
22271             
22272             
22273         }
22274         this.execCmd("formatblock",   tg);
22275         
22276     },
22277     
22278     insertText : function(txt)
22279     {
22280         
22281         
22282         var range = this.createRange();
22283         range.deleteContents();
22284                //alert(Sender.getAttribute('label'));
22285                
22286         range.insertNode(this.doc.createTextNode(txt));
22287     } ,
22288     
22289      
22290
22291     /**
22292      * Executes a Midas editor command on the editor document and performs necessary focus and
22293      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22294      * @param {String} cmd The Midas command
22295      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22296      */
22297     relayCmd : function(cmd, value){
22298         this.win.focus();
22299         this.execCmd(cmd, value);
22300         this.owner.fireEvent('editorevent', this);
22301         //this.updateToolbar();
22302         this.owner.deferFocus();
22303     },
22304
22305     /**
22306      * Executes a Midas editor command directly on the editor document.
22307      * For visual commands, you should use {@link #relayCmd} instead.
22308      * <b>This should only be called after the editor is initialized.</b>
22309      * @param {String} cmd The Midas command
22310      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22311      */
22312     execCmd : function(cmd, value){
22313         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22314         this.syncValue();
22315     },
22316  
22317  
22318    
22319     /**
22320      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22321      * to insert tRoo.
22322      * @param {String} text | dom node.. 
22323      */
22324     insertAtCursor : function(text)
22325     {
22326         
22327         if(!this.activated){
22328             return;
22329         }
22330         /*
22331         if(Roo.isIE){
22332             this.win.focus();
22333             var r = this.doc.selection.createRange();
22334             if(r){
22335                 r.collapse(true);
22336                 r.pasteHTML(text);
22337                 this.syncValue();
22338                 this.deferFocus();
22339             
22340             }
22341             return;
22342         }
22343         */
22344         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22345             this.win.focus();
22346             
22347             
22348             // from jquery ui (MIT licenced)
22349             var range, node;
22350             var win = this.win;
22351             
22352             if (win.getSelection && win.getSelection().getRangeAt) {
22353                 range = win.getSelection().getRangeAt(0);
22354                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22355                 range.insertNode(node);
22356             } else if (win.document.selection && win.document.selection.createRange) {
22357                 // no firefox support
22358                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22359                 win.document.selection.createRange().pasteHTML(txt);
22360             } else {
22361                 // no firefox support
22362                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22363                 this.execCmd('InsertHTML', txt);
22364             } 
22365             
22366             this.syncValue();
22367             
22368             this.deferFocus();
22369         }
22370     },
22371  // private
22372     mozKeyPress : function(e){
22373         if(e.ctrlKey){
22374             var c = e.getCharCode(), cmd;
22375           
22376             if(c > 0){
22377                 c = String.fromCharCode(c).toLowerCase();
22378                 switch(c){
22379                     case 'b':
22380                         cmd = 'bold';
22381                         break;
22382                     case 'i':
22383                         cmd = 'italic';
22384                         break;
22385                     
22386                     case 'u':
22387                         cmd = 'underline';
22388                         break;
22389                     
22390                     case 'v':
22391                         this.cleanUpPaste.defer(100, this);
22392                         return;
22393                         
22394                 }
22395                 if(cmd){
22396                     this.win.focus();
22397                     this.execCmd(cmd);
22398                     this.deferFocus();
22399                     e.preventDefault();
22400                 }
22401                 
22402             }
22403         }
22404     },
22405
22406     // private
22407     fixKeys : function(){ // load time branching for fastest keydown performance
22408         if(Roo.isIE){
22409             return function(e){
22410                 var k = e.getKey(), r;
22411                 if(k == e.TAB){
22412                     e.stopEvent();
22413                     r = this.doc.selection.createRange();
22414                     if(r){
22415                         r.collapse(true);
22416                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22417                         this.deferFocus();
22418                     }
22419                     return;
22420                 }
22421                 
22422                 if(k == e.ENTER){
22423                     r = this.doc.selection.createRange();
22424                     if(r){
22425                         var target = r.parentElement();
22426                         if(!target || target.tagName.toLowerCase() != 'li'){
22427                             e.stopEvent();
22428                             r.pasteHTML('<br />');
22429                             r.collapse(false);
22430                             r.select();
22431                         }
22432                     }
22433                 }
22434                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22435                     this.cleanUpPaste.defer(100, this);
22436                     return;
22437                 }
22438                 
22439                 
22440             };
22441         }else if(Roo.isOpera){
22442             return function(e){
22443                 var k = e.getKey();
22444                 if(k == e.TAB){
22445                     e.stopEvent();
22446                     this.win.focus();
22447                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22448                     this.deferFocus();
22449                 }
22450                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22451                     this.cleanUpPaste.defer(100, this);
22452                     return;
22453                 }
22454                 
22455             };
22456         }else if(Roo.isSafari){
22457             return function(e){
22458                 var k = e.getKey();
22459                 
22460                 if(k == e.TAB){
22461                     e.stopEvent();
22462                     this.execCmd('InsertText','\t');
22463                     this.deferFocus();
22464                     return;
22465                 }
22466                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22467                     this.cleanUpPaste.defer(100, this);
22468                     return;
22469                 }
22470                 
22471              };
22472         }
22473     }(),
22474     
22475     getAllAncestors: function()
22476     {
22477         var p = this.getSelectedNode();
22478         var a = [];
22479         if (!p) {
22480             a.push(p); // push blank onto stack..
22481             p = this.getParentElement();
22482         }
22483         
22484         
22485         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22486             a.push(p);
22487             p = p.parentNode;
22488         }
22489         a.push(this.doc.body);
22490         return a;
22491     },
22492     lastSel : false,
22493     lastSelNode : false,
22494     
22495     
22496     getSelection : function() 
22497     {
22498         this.assignDocWin();
22499         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22500     },
22501     
22502     getSelectedNode: function() 
22503     {
22504         // this may only work on Gecko!!!
22505         
22506         // should we cache this!!!!
22507         
22508         
22509         
22510          
22511         var range = this.createRange(this.getSelection()).cloneRange();
22512         
22513         if (Roo.isIE) {
22514             var parent = range.parentElement();
22515             while (true) {
22516                 var testRange = range.duplicate();
22517                 testRange.moveToElementText(parent);
22518                 if (testRange.inRange(range)) {
22519                     break;
22520                 }
22521                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22522                     break;
22523                 }
22524                 parent = parent.parentElement;
22525             }
22526             return parent;
22527         }
22528         
22529         // is ancestor a text element.
22530         var ac =  range.commonAncestorContainer;
22531         if (ac.nodeType == 3) {
22532             ac = ac.parentNode;
22533         }
22534         
22535         var ar = ac.childNodes;
22536          
22537         var nodes = [];
22538         var other_nodes = [];
22539         var has_other_nodes = false;
22540         for (var i=0;i<ar.length;i++) {
22541             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22542                 continue;
22543             }
22544             // fullly contained node.
22545             
22546             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22547                 nodes.push(ar[i]);
22548                 continue;
22549             }
22550             
22551             // probably selected..
22552             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22553                 other_nodes.push(ar[i]);
22554                 continue;
22555             }
22556             // outer..
22557             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22558                 continue;
22559             }
22560             
22561             
22562             has_other_nodes = true;
22563         }
22564         if (!nodes.length && other_nodes.length) {
22565             nodes= other_nodes;
22566         }
22567         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22568             return false;
22569         }
22570         
22571         return nodes[0];
22572     },
22573     createRange: function(sel)
22574     {
22575         // this has strange effects when using with 
22576         // top toolbar - not sure if it's a great idea.
22577         //this.editor.contentWindow.focus();
22578         if (typeof sel != "undefined") {
22579             try {
22580                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22581             } catch(e) {
22582                 return this.doc.createRange();
22583             }
22584         } else {
22585             return this.doc.createRange();
22586         }
22587     },
22588     getParentElement: function()
22589     {
22590         
22591         this.assignDocWin();
22592         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22593         
22594         var range = this.createRange(sel);
22595          
22596         try {
22597             var p = range.commonAncestorContainer;
22598             while (p.nodeType == 3) { // text node
22599                 p = p.parentNode;
22600             }
22601             return p;
22602         } catch (e) {
22603             return null;
22604         }
22605     
22606     },
22607     /***
22608      *
22609      * Range intersection.. the hard stuff...
22610      *  '-1' = before
22611      *  '0' = hits..
22612      *  '1' = after.
22613      *         [ -- selected range --- ]
22614      *   [fail]                        [fail]
22615      *
22616      *    basically..
22617      *      if end is before start or  hits it. fail.
22618      *      if start is after end or hits it fail.
22619      *
22620      *   if either hits (but other is outside. - then it's not 
22621      *   
22622      *    
22623      **/
22624     
22625     
22626     // @see http://www.thismuchiknow.co.uk/?p=64.
22627     rangeIntersectsNode : function(range, node)
22628     {
22629         var nodeRange = node.ownerDocument.createRange();
22630         try {
22631             nodeRange.selectNode(node);
22632         } catch (e) {
22633             nodeRange.selectNodeContents(node);
22634         }
22635     
22636         var rangeStartRange = range.cloneRange();
22637         rangeStartRange.collapse(true);
22638     
22639         var rangeEndRange = range.cloneRange();
22640         rangeEndRange.collapse(false);
22641     
22642         var nodeStartRange = nodeRange.cloneRange();
22643         nodeStartRange.collapse(true);
22644     
22645         var nodeEndRange = nodeRange.cloneRange();
22646         nodeEndRange.collapse(false);
22647     
22648         return rangeStartRange.compareBoundaryPoints(
22649                  Range.START_TO_START, nodeEndRange) == -1 &&
22650                rangeEndRange.compareBoundaryPoints(
22651                  Range.START_TO_START, nodeStartRange) == 1;
22652         
22653          
22654     },
22655     rangeCompareNode : function(range, node)
22656     {
22657         var nodeRange = node.ownerDocument.createRange();
22658         try {
22659             nodeRange.selectNode(node);
22660         } catch (e) {
22661             nodeRange.selectNodeContents(node);
22662         }
22663         
22664         
22665         range.collapse(true);
22666     
22667         nodeRange.collapse(true);
22668      
22669         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22670         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22671          
22672         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22673         
22674         var nodeIsBefore   =  ss == 1;
22675         var nodeIsAfter    = ee == -1;
22676         
22677         if (nodeIsBefore && nodeIsAfter) {
22678             return 0; // outer
22679         }
22680         if (!nodeIsBefore && nodeIsAfter) {
22681             return 1; //right trailed.
22682         }
22683         
22684         if (nodeIsBefore && !nodeIsAfter) {
22685             return 2;  // left trailed.
22686         }
22687         // fully contined.
22688         return 3;
22689     },
22690
22691     // private? - in a new class?
22692     cleanUpPaste :  function()
22693     {
22694         // cleans up the whole document..
22695         Roo.log('cleanuppaste');
22696         
22697         this.cleanUpChildren(this.doc.body);
22698         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22699         if (clean != this.doc.body.innerHTML) {
22700             this.doc.body.innerHTML = clean;
22701         }
22702         
22703     },
22704     
22705     cleanWordChars : function(input) {// change the chars to hex code
22706         var he = Roo.HtmlEditorCore;
22707         
22708         var output = input;
22709         Roo.each(he.swapCodes, function(sw) { 
22710             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22711             
22712             output = output.replace(swapper, sw[1]);
22713         });
22714         
22715         return output;
22716     },
22717     
22718     
22719     cleanUpChildren : function (n)
22720     {
22721         if (!n.childNodes.length) {
22722             return;
22723         }
22724         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22725            this.cleanUpChild(n.childNodes[i]);
22726         }
22727     },
22728     
22729     
22730         
22731     
22732     cleanUpChild : function (node)
22733     {
22734         var ed = this;
22735         //console.log(node);
22736         if (node.nodeName == "#text") {
22737             // clean up silly Windows -- stuff?
22738             return; 
22739         }
22740         if (node.nodeName == "#comment") {
22741             node.parentNode.removeChild(node);
22742             // clean up silly Windows -- stuff?
22743             return; 
22744         }
22745         var lcname = node.tagName.toLowerCase();
22746         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22747         // whitelist of tags..
22748         
22749         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22750             // remove node.
22751             node.parentNode.removeChild(node);
22752             return;
22753             
22754         }
22755         
22756         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22757         
22758         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22759         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22760         
22761         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22762         //    remove_keep_children = true;
22763         //}
22764         
22765         if (remove_keep_children) {
22766             this.cleanUpChildren(node);
22767             // inserts everything just before this node...
22768             while (node.childNodes.length) {
22769                 var cn = node.childNodes[0];
22770                 node.removeChild(cn);
22771                 node.parentNode.insertBefore(cn, node);
22772             }
22773             node.parentNode.removeChild(node);
22774             return;
22775         }
22776         
22777         if (!node.attributes || !node.attributes.length) {
22778             this.cleanUpChildren(node);
22779             return;
22780         }
22781         
22782         function cleanAttr(n,v)
22783         {
22784             
22785             if (v.match(/^\./) || v.match(/^\//)) {
22786                 return;
22787             }
22788             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22789                 return;
22790             }
22791             if (v.match(/^#/)) {
22792                 return;
22793             }
22794 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22795             node.removeAttribute(n);
22796             
22797         }
22798         
22799         var cwhite = this.cwhite;
22800         var cblack = this.cblack;
22801             
22802         function cleanStyle(n,v)
22803         {
22804             if (v.match(/expression/)) { //XSS?? should we even bother..
22805                 node.removeAttribute(n);
22806                 return;
22807             }
22808             
22809             var parts = v.split(/;/);
22810             var clean = [];
22811             
22812             Roo.each(parts, function(p) {
22813                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22814                 if (!p.length) {
22815                     return true;
22816                 }
22817                 var l = p.split(':').shift().replace(/\s+/g,'');
22818                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22819                 
22820                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22821 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22822                     //node.removeAttribute(n);
22823                     return true;
22824                 }
22825                 //Roo.log()
22826                 // only allow 'c whitelisted system attributes'
22827                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22828 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22829                     //node.removeAttribute(n);
22830                     return true;
22831                 }
22832                 
22833                 
22834                  
22835                 
22836                 clean.push(p);
22837                 return true;
22838             });
22839             if (clean.length) { 
22840                 node.setAttribute(n, clean.join(';'));
22841             } else {
22842                 node.removeAttribute(n);
22843             }
22844             
22845         }
22846         
22847         
22848         for (var i = node.attributes.length-1; i > -1 ; i--) {
22849             var a = node.attributes[i];
22850             //console.log(a);
22851             
22852             if (a.name.toLowerCase().substr(0,2)=='on')  {
22853                 node.removeAttribute(a.name);
22854                 continue;
22855             }
22856             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22857                 node.removeAttribute(a.name);
22858                 continue;
22859             }
22860             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22861                 cleanAttr(a.name,a.value); // fixme..
22862                 continue;
22863             }
22864             if (a.name == 'style') {
22865                 cleanStyle(a.name,a.value);
22866                 continue;
22867             }
22868             /// clean up MS crap..
22869             // tecnically this should be a list of valid class'es..
22870             
22871             
22872             if (a.name == 'class') {
22873                 if (a.value.match(/^Mso/)) {
22874                     node.className = '';
22875                 }
22876                 
22877                 if (a.value.match(/^body$/)) {
22878                     node.className = '';
22879                 }
22880                 continue;
22881             }
22882             
22883             // style cleanup!?
22884             // class cleanup?
22885             
22886         }
22887         
22888         
22889         this.cleanUpChildren(node);
22890         
22891         
22892     },
22893     
22894     /**
22895      * Clean up MS wordisms...
22896      */
22897     cleanWord : function(node)
22898     {
22899         
22900         
22901         if (!node) {
22902             this.cleanWord(this.doc.body);
22903             return;
22904         }
22905         if (node.nodeName == "#text") {
22906             // clean up silly Windows -- stuff?
22907             return; 
22908         }
22909         if (node.nodeName == "#comment") {
22910             node.parentNode.removeChild(node);
22911             // clean up silly Windows -- stuff?
22912             return; 
22913         }
22914         
22915         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22916             node.parentNode.removeChild(node);
22917             return;
22918         }
22919         
22920         // remove - but keep children..
22921         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22922             while (node.childNodes.length) {
22923                 var cn = node.childNodes[0];
22924                 node.removeChild(cn);
22925                 node.parentNode.insertBefore(cn, node);
22926             }
22927             node.parentNode.removeChild(node);
22928             this.iterateChildren(node, this.cleanWord);
22929             return;
22930         }
22931         // clean styles
22932         if (node.className.length) {
22933             
22934             var cn = node.className.split(/\W+/);
22935             var cna = [];
22936             Roo.each(cn, function(cls) {
22937                 if (cls.match(/Mso[a-zA-Z]+/)) {
22938                     return;
22939                 }
22940                 cna.push(cls);
22941             });
22942             node.className = cna.length ? cna.join(' ') : '';
22943             if (!cna.length) {
22944                 node.removeAttribute("class");
22945             }
22946         }
22947         
22948         if (node.hasAttribute("lang")) {
22949             node.removeAttribute("lang");
22950         }
22951         
22952         if (node.hasAttribute("style")) {
22953             
22954             var styles = node.getAttribute("style").split(";");
22955             var nstyle = [];
22956             Roo.each(styles, function(s) {
22957                 if (!s.match(/:/)) {
22958                     return;
22959                 }
22960                 var kv = s.split(":");
22961                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22962                     return;
22963                 }
22964                 // what ever is left... we allow.
22965                 nstyle.push(s);
22966             });
22967             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22968             if (!nstyle.length) {
22969                 node.removeAttribute('style');
22970             }
22971         }
22972         this.iterateChildren(node, this.cleanWord);
22973         
22974         
22975         
22976     },
22977     /**
22978      * iterateChildren of a Node, calling fn each time, using this as the scole..
22979      * @param {DomNode} node node to iterate children of.
22980      * @param {Function} fn method of this class to call on each item.
22981      */
22982     iterateChildren : function(node, fn)
22983     {
22984         if (!node.childNodes.length) {
22985                 return;
22986         }
22987         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22988            fn.call(this, node.childNodes[i])
22989         }
22990     },
22991     
22992     
22993     /**
22994      * cleanTableWidths.
22995      *
22996      * Quite often pasting from word etc.. results in tables with column and widths.
22997      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22998      *
22999      */
23000     cleanTableWidths : function(node)
23001     {
23002          
23003          
23004         if (!node) {
23005             this.cleanTableWidths(this.doc.body);
23006             return;
23007         }
23008         
23009         // ignore list...
23010         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23011             return; 
23012         }
23013         Roo.log(node.tagName);
23014         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23015             this.iterateChildren(node, this.cleanTableWidths);
23016             return;
23017         }
23018         if (node.hasAttribute('width')) {
23019             node.removeAttribute('width');
23020         }
23021         
23022          
23023         if (node.hasAttribute("style")) {
23024             // pretty basic...
23025             
23026             var styles = node.getAttribute("style").split(";");
23027             var nstyle = [];
23028             Roo.each(styles, function(s) {
23029                 if (!s.match(/:/)) {
23030                     return;
23031                 }
23032                 var kv = s.split(":");
23033                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23034                     return;
23035                 }
23036                 // what ever is left... we allow.
23037                 nstyle.push(s);
23038             });
23039             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23040             if (!nstyle.length) {
23041                 node.removeAttribute('style');
23042             }
23043         }
23044         
23045         this.iterateChildren(node, this.cleanTableWidths);
23046         
23047         
23048     },
23049     
23050     
23051     
23052     
23053     domToHTML : function(currentElement, depth, nopadtext) {
23054         
23055         depth = depth || 0;
23056         nopadtext = nopadtext || false;
23057     
23058         if (!currentElement) {
23059             return this.domToHTML(this.doc.body);
23060         }
23061         
23062         //Roo.log(currentElement);
23063         var j;
23064         var allText = false;
23065         var nodeName = currentElement.nodeName;
23066         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23067         
23068         if  (nodeName == '#text') {
23069             
23070             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23071         }
23072         
23073         
23074         var ret = '';
23075         if (nodeName != 'BODY') {
23076              
23077             var i = 0;
23078             // Prints the node tagName, such as <A>, <IMG>, etc
23079             if (tagName) {
23080                 var attr = [];
23081                 for(i = 0; i < currentElement.attributes.length;i++) {
23082                     // quoting?
23083                     var aname = currentElement.attributes.item(i).name;
23084                     if (!currentElement.attributes.item(i).value.length) {
23085                         continue;
23086                     }
23087                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23088                 }
23089                 
23090                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23091             } 
23092             else {
23093                 
23094                 // eack
23095             }
23096         } else {
23097             tagName = false;
23098         }
23099         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23100             return ret;
23101         }
23102         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23103             nopadtext = true;
23104         }
23105         
23106         
23107         // Traverse the tree
23108         i = 0;
23109         var currentElementChild = currentElement.childNodes.item(i);
23110         var allText = true;
23111         var innerHTML  = '';
23112         lastnode = '';
23113         while (currentElementChild) {
23114             // Formatting code (indent the tree so it looks nice on the screen)
23115             var nopad = nopadtext;
23116             if (lastnode == 'SPAN') {
23117                 nopad  = true;
23118             }
23119             // text
23120             if  (currentElementChild.nodeName == '#text') {
23121                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23122                 toadd = nopadtext ? toadd : toadd.trim();
23123                 if (!nopad && toadd.length > 80) {
23124                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23125                 }
23126                 innerHTML  += toadd;
23127                 
23128                 i++;
23129                 currentElementChild = currentElement.childNodes.item(i);
23130                 lastNode = '';
23131                 continue;
23132             }
23133             allText = false;
23134             
23135             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23136                 
23137             // Recursively traverse the tree structure of the child node
23138             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23139             lastnode = currentElementChild.nodeName;
23140             i++;
23141             currentElementChild=currentElement.childNodes.item(i);
23142         }
23143         
23144         ret += innerHTML;
23145         
23146         if (!allText) {
23147                 // The remaining code is mostly for formatting the tree
23148             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23149         }
23150         
23151         
23152         if (tagName) {
23153             ret+= "</"+tagName+">";
23154         }
23155         return ret;
23156         
23157     },
23158         
23159     applyBlacklists : function()
23160     {
23161         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23162         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23163         
23164         this.white = [];
23165         this.black = [];
23166         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23167             if (b.indexOf(tag) > -1) {
23168                 return;
23169             }
23170             this.white.push(tag);
23171             
23172         }, this);
23173         
23174         Roo.each(w, function(tag) {
23175             if (b.indexOf(tag) > -1) {
23176                 return;
23177             }
23178             if (this.white.indexOf(tag) > -1) {
23179                 return;
23180             }
23181             this.white.push(tag);
23182             
23183         }, this);
23184         
23185         
23186         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23187             if (w.indexOf(tag) > -1) {
23188                 return;
23189             }
23190             this.black.push(tag);
23191             
23192         }, this);
23193         
23194         Roo.each(b, function(tag) {
23195             if (w.indexOf(tag) > -1) {
23196                 return;
23197             }
23198             if (this.black.indexOf(tag) > -1) {
23199                 return;
23200             }
23201             this.black.push(tag);
23202             
23203         }, this);
23204         
23205         
23206         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23207         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23208         
23209         this.cwhite = [];
23210         this.cblack = [];
23211         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23212             if (b.indexOf(tag) > -1) {
23213                 return;
23214             }
23215             this.cwhite.push(tag);
23216             
23217         }, this);
23218         
23219         Roo.each(w, function(tag) {
23220             if (b.indexOf(tag) > -1) {
23221                 return;
23222             }
23223             if (this.cwhite.indexOf(tag) > -1) {
23224                 return;
23225             }
23226             this.cwhite.push(tag);
23227             
23228         }, this);
23229         
23230         
23231         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23232             if (w.indexOf(tag) > -1) {
23233                 return;
23234             }
23235             this.cblack.push(tag);
23236             
23237         }, this);
23238         
23239         Roo.each(b, function(tag) {
23240             if (w.indexOf(tag) > -1) {
23241                 return;
23242             }
23243             if (this.cblack.indexOf(tag) > -1) {
23244                 return;
23245             }
23246             this.cblack.push(tag);
23247             
23248         }, this);
23249     },
23250     
23251     setStylesheets : function(stylesheets)
23252     {
23253         if(typeof(stylesheets) == 'string'){
23254             Roo.get(this.iframe.contentDocument.head).createChild({
23255                 tag : 'link',
23256                 rel : 'stylesheet',
23257                 type : 'text/css',
23258                 href : stylesheets
23259             });
23260             
23261             return;
23262         }
23263         var _this = this;
23264      
23265         Roo.each(stylesheets, function(s) {
23266             if(!s.length){
23267                 return;
23268             }
23269             
23270             Roo.get(_this.iframe.contentDocument.head).createChild({
23271                 tag : 'link',
23272                 rel : 'stylesheet',
23273                 type : 'text/css',
23274                 href : s
23275             });
23276         });
23277
23278         
23279     },
23280     
23281     removeStylesheets : function()
23282     {
23283         var _this = this;
23284         
23285         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23286             s.remove();
23287         });
23288     },
23289     
23290     setStyle : function(style)
23291     {
23292         Roo.get(this.iframe.contentDocument.head).createChild({
23293             tag : 'style',
23294             type : 'text/css',
23295             html : style
23296         });
23297
23298         return;
23299     }
23300     
23301     // hide stuff that is not compatible
23302     /**
23303      * @event blur
23304      * @hide
23305      */
23306     /**
23307      * @event change
23308      * @hide
23309      */
23310     /**
23311      * @event focus
23312      * @hide
23313      */
23314     /**
23315      * @event specialkey
23316      * @hide
23317      */
23318     /**
23319      * @cfg {String} fieldClass @hide
23320      */
23321     /**
23322      * @cfg {String} focusClass @hide
23323      */
23324     /**
23325      * @cfg {String} autoCreate @hide
23326      */
23327     /**
23328      * @cfg {String} inputType @hide
23329      */
23330     /**
23331      * @cfg {String} invalidClass @hide
23332      */
23333     /**
23334      * @cfg {String} invalidText @hide
23335      */
23336     /**
23337      * @cfg {String} msgFx @hide
23338      */
23339     /**
23340      * @cfg {String} validateOnBlur @hide
23341      */
23342 });
23343
23344 Roo.HtmlEditorCore.white = [
23345         'area', 'br', 'img', 'input', 'hr', 'wbr',
23346         
23347        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23348        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23349        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23350        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23351        'table',   'ul',         'xmp', 
23352        
23353        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23354       'thead',   'tr', 
23355      
23356       'dir', 'menu', 'ol', 'ul', 'dl',
23357        
23358       'embed',  'object'
23359 ];
23360
23361
23362 Roo.HtmlEditorCore.black = [
23363     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23364         'applet', // 
23365         'base',   'basefont', 'bgsound', 'blink',  'body', 
23366         'frame',  'frameset', 'head',    'html',   'ilayer', 
23367         'iframe', 'layer',  'link',     'meta',    'object',   
23368         'script', 'style' ,'title',  'xml' // clean later..
23369 ];
23370 Roo.HtmlEditorCore.clean = [
23371     'script', 'style', 'title', 'xml'
23372 ];
23373 Roo.HtmlEditorCore.remove = [
23374     'font'
23375 ];
23376 // attributes..
23377
23378 Roo.HtmlEditorCore.ablack = [
23379     'on'
23380 ];
23381     
23382 Roo.HtmlEditorCore.aclean = [ 
23383     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23384 ];
23385
23386 // protocols..
23387 Roo.HtmlEditorCore.pwhite= [
23388         'http',  'https',  'mailto'
23389 ];
23390
23391 // white listed style attributes.
23392 Roo.HtmlEditorCore.cwhite= [
23393       //  'text-align', /// default is to allow most things..
23394       
23395          
23396 //        'font-size'//??
23397 ];
23398
23399 // black listed style attributes.
23400 Roo.HtmlEditorCore.cblack= [
23401       //  'font-size' -- this can be set by the project 
23402 ];
23403
23404
23405 Roo.HtmlEditorCore.swapCodes   =[ 
23406     [    8211, "--" ], 
23407     [    8212, "--" ], 
23408     [    8216,  "'" ],  
23409     [    8217, "'" ],  
23410     [    8220, '"' ],  
23411     [    8221, '"' ],  
23412     [    8226, "*" ],  
23413     [    8230, "..." ]
23414 ]; 
23415
23416     /*
23417  * - LGPL
23418  *
23419  * HtmlEditor
23420  * 
23421  */
23422
23423 /**
23424  * @class Roo.bootstrap.HtmlEditor
23425  * @extends Roo.bootstrap.TextArea
23426  * Bootstrap HtmlEditor class
23427
23428  * @constructor
23429  * Create a new HtmlEditor
23430  * @param {Object} config The config object
23431  */
23432
23433 Roo.bootstrap.HtmlEditor = function(config){
23434     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23435     if (!this.toolbars) {
23436         this.toolbars = [];
23437     }
23438     
23439     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23440     this.addEvents({
23441             /**
23442              * @event initialize
23443              * Fires when the editor is fully initialized (including the iframe)
23444              * @param {HtmlEditor} this
23445              */
23446             initialize: true,
23447             /**
23448              * @event activate
23449              * Fires when the editor is first receives the focus. Any insertion must wait
23450              * until after this event.
23451              * @param {HtmlEditor} this
23452              */
23453             activate: true,
23454              /**
23455              * @event beforesync
23456              * Fires before the textarea is updated with content from the editor iframe. Return false
23457              * to cancel the sync.
23458              * @param {HtmlEditor} this
23459              * @param {String} html
23460              */
23461             beforesync: true,
23462              /**
23463              * @event beforepush
23464              * Fires before the iframe editor is updated with content from the textarea. Return false
23465              * to cancel the push.
23466              * @param {HtmlEditor} this
23467              * @param {String} html
23468              */
23469             beforepush: true,
23470              /**
23471              * @event sync
23472              * Fires when the textarea is updated with content from the editor iframe.
23473              * @param {HtmlEditor} this
23474              * @param {String} html
23475              */
23476             sync: true,
23477              /**
23478              * @event push
23479              * Fires when the iframe editor is updated with content from the textarea.
23480              * @param {HtmlEditor} this
23481              * @param {String} html
23482              */
23483             push: true,
23484              /**
23485              * @event editmodechange
23486              * Fires when the editor switches edit modes
23487              * @param {HtmlEditor} this
23488              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23489              */
23490             editmodechange: true,
23491             /**
23492              * @event editorevent
23493              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23494              * @param {HtmlEditor} this
23495              */
23496             editorevent: true,
23497             /**
23498              * @event firstfocus
23499              * Fires when on first focus - needed by toolbars..
23500              * @param {HtmlEditor} this
23501              */
23502             firstfocus: true,
23503             /**
23504              * @event autosave
23505              * Auto save the htmlEditor value as a file into Events
23506              * @param {HtmlEditor} this
23507              */
23508             autosave: true,
23509             /**
23510              * @event savedpreview
23511              * preview the saved version of htmlEditor
23512              * @param {HtmlEditor} this
23513              */
23514             savedpreview: true
23515         });
23516 };
23517
23518
23519 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23520     
23521     
23522       /**
23523      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23524      */
23525     toolbars : false,
23526     
23527      /**
23528     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23529     */
23530     btns : [],
23531    
23532      /**
23533      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23534      *                        Roo.resizable.
23535      */
23536     resizable : false,
23537      /**
23538      * @cfg {Number} height (in pixels)
23539      */   
23540     height: 300,
23541    /**
23542      * @cfg {Number} width (in pixels)
23543      */   
23544     width: false,
23545     
23546     /**
23547      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23548      * 
23549      */
23550     stylesheets: false,
23551     
23552     // id of frame..
23553     frameId: false,
23554     
23555     // private properties
23556     validationEvent : false,
23557     deferHeight: true,
23558     initialized : false,
23559     activated : false,
23560     
23561     onFocus : Roo.emptyFn,
23562     iframePad:3,
23563     hideMode:'offsets',
23564     
23565     tbContainer : false,
23566     
23567     bodyCls : '',
23568     
23569     toolbarContainer :function() {
23570         return this.wrap.select('.x-html-editor-tb',true).first();
23571     },
23572
23573     /**
23574      * Protected method that will not generally be called directly. It
23575      * is called when the editor creates its toolbar. Override this method if you need to
23576      * add custom toolbar buttons.
23577      * @param {HtmlEditor} editor
23578      */
23579     createToolbar : function(){
23580         Roo.log('renewing');
23581         Roo.log("create toolbars");
23582         
23583         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23584         this.toolbars[0].render(this.toolbarContainer());
23585         
23586         return;
23587         
23588 //        if (!editor.toolbars || !editor.toolbars.length) {
23589 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23590 //        }
23591 //        
23592 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23593 //            editor.toolbars[i] = Roo.factory(
23594 //                    typeof(editor.toolbars[i]) == 'string' ?
23595 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23596 //                Roo.bootstrap.HtmlEditor);
23597 //            editor.toolbars[i].init(editor);
23598 //        }
23599     },
23600
23601      
23602     // private
23603     onRender : function(ct, position)
23604     {
23605        // Roo.log("Call onRender: " + this.xtype);
23606         var _t = this;
23607         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23608       
23609         this.wrap = this.inputEl().wrap({
23610             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23611         });
23612         
23613         this.editorcore.onRender(ct, position);
23614          
23615         if (this.resizable) {
23616             this.resizeEl = new Roo.Resizable(this.wrap, {
23617                 pinned : true,
23618                 wrap: true,
23619                 dynamic : true,
23620                 minHeight : this.height,
23621                 height: this.height,
23622                 handles : this.resizable,
23623                 width: this.width,
23624                 listeners : {
23625                     resize : function(r, w, h) {
23626                         _t.onResize(w,h); // -something
23627                     }
23628                 }
23629             });
23630             
23631         }
23632         this.createToolbar(this);
23633        
23634         
23635         if(!this.width && this.resizable){
23636             this.setSize(this.wrap.getSize());
23637         }
23638         if (this.resizeEl) {
23639             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23640             // should trigger onReize..
23641         }
23642         
23643     },
23644
23645     // private
23646     onResize : function(w, h)
23647     {
23648         Roo.log('resize: ' +w + ',' + h );
23649         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23650         var ew = false;
23651         var eh = false;
23652         
23653         if(this.inputEl() ){
23654             if(typeof w == 'number'){
23655                 var aw = w - this.wrap.getFrameWidth('lr');
23656                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23657                 ew = aw;
23658             }
23659             if(typeof h == 'number'){
23660                  var tbh = -11;  // fixme it needs to tool bar size!
23661                 for (var i =0; i < this.toolbars.length;i++) {
23662                     // fixme - ask toolbars for heights?
23663                     tbh += this.toolbars[i].el.getHeight();
23664                     //if (this.toolbars[i].footer) {
23665                     //    tbh += this.toolbars[i].footer.el.getHeight();
23666                     //}
23667                 }
23668               
23669                 
23670                 
23671                 
23672                 
23673                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23674                 ah -= 5; // knock a few pixes off for look..
23675                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23676                 var eh = ah;
23677             }
23678         }
23679         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23680         this.editorcore.onResize(ew,eh);
23681         
23682     },
23683
23684     /**
23685      * Toggles the editor between standard and source edit mode.
23686      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23687      */
23688     toggleSourceEdit : function(sourceEditMode)
23689     {
23690         this.editorcore.toggleSourceEdit(sourceEditMode);
23691         
23692         if(this.editorcore.sourceEditMode){
23693             Roo.log('editor - showing textarea');
23694             
23695 //            Roo.log('in');
23696 //            Roo.log(this.syncValue());
23697             this.syncValue();
23698             this.inputEl().removeClass(['hide', 'x-hidden']);
23699             this.inputEl().dom.removeAttribute('tabIndex');
23700             this.inputEl().focus();
23701         }else{
23702             Roo.log('editor - hiding textarea');
23703 //            Roo.log('out')
23704 //            Roo.log(this.pushValue()); 
23705             this.pushValue();
23706             
23707             this.inputEl().addClass(['hide', 'x-hidden']);
23708             this.inputEl().dom.setAttribute('tabIndex', -1);
23709             //this.deferFocus();
23710         }
23711          
23712         if(this.resizable){
23713             this.setSize(this.wrap.getSize());
23714         }
23715         
23716         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23717     },
23718  
23719     // private (for BoxComponent)
23720     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23721
23722     // private (for BoxComponent)
23723     getResizeEl : function(){
23724         return this.wrap;
23725     },
23726
23727     // private (for BoxComponent)
23728     getPositionEl : function(){
23729         return this.wrap;
23730     },
23731
23732     // private
23733     initEvents : function(){
23734         this.originalValue = this.getValue();
23735     },
23736
23737 //    /**
23738 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23739 //     * @method
23740 //     */
23741 //    markInvalid : Roo.emptyFn,
23742 //    /**
23743 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23744 //     * @method
23745 //     */
23746 //    clearInvalid : Roo.emptyFn,
23747
23748     setValue : function(v){
23749         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23750         this.editorcore.pushValue();
23751     },
23752
23753      
23754     // private
23755     deferFocus : function(){
23756         this.focus.defer(10, this);
23757     },
23758
23759     // doc'ed in Field
23760     focus : function(){
23761         this.editorcore.focus();
23762         
23763     },
23764       
23765
23766     // private
23767     onDestroy : function(){
23768         
23769         
23770         
23771         if(this.rendered){
23772             
23773             for (var i =0; i < this.toolbars.length;i++) {
23774                 // fixme - ask toolbars for heights?
23775                 this.toolbars[i].onDestroy();
23776             }
23777             
23778             this.wrap.dom.innerHTML = '';
23779             this.wrap.remove();
23780         }
23781     },
23782
23783     // private
23784     onFirstFocus : function(){
23785         //Roo.log("onFirstFocus");
23786         this.editorcore.onFirstFocus();
23787          for (var i =0; i < this.toolbars.length;i++) {
23788             this.toolbars[i].onFirstFocus();
23789         }
23790         
23791     },
23792     
23793     // private
23794     syncValue : function()
23795     {   
23796         this.editorcore.syncValue();
23797     },
23798     
23799     pushValue : function()
23800     {   
23801         this.editorcore.pushValue();
23802     }
23803      
23804     
23805     // hide stuff that is not compatible
23806     /**
23807      * @event blur
23808      * @hide
23809      */
23810     /**
23811      * @event change
23812      * @hide
23813      */
23814     /**
23815      * @event focus
23816      * @hide
23817      */
23818     /**
23819      * @event specialkey
23820      * @hide
23821      */
23822     /**
23823      * @cfg {String} fieldClass @hide
23824      */
23825     /**
23826      * @cfg {String} focusClass @hide
23827      */
23828     /**
23829      * @cfg {String} autoCreate @hide
23830      */
23831     /**
23832      * @cfg {String} inputType @hide
23833      */
23834     /**
23835      * @cfg {String} invalidClass @hide
23836      */
23837     /**
23838      * @cfg {String} invalidText @hide
23839      */
23840     /**
23841      * @cfg {String} msgFx @hide
23842      */
23843     /**
23844      * @cfg {String} validateOnBlur @hide
23845      */
23846 });
23847  
23848     
23849    
23850    
23851    
23852       
23853 Roo.namespace('Roo.bootstrap.htmleditor');
23854 /**
23855  * @class Roo.bootstrap.HtmlEditorToolbar1
23856  * Basic Toolbar
23857  * 
23858  * Usage:
23859  *
23860  new Roo.bootstrap.HtmlEditor({
23861     ....
23862     toolbars : [
23863         new Roo.bootstrap.HtmlEditorToolbar1({
23864             disable : { fonts: 1 , format: 1, ..., ... , ...],
23865             btns : [ .... ]
23866         })
23867     }
23868      
23869  * 
23870  * @cfg {Object} disable List of elements to disable..
23871  * @cfg {Array} btns List of additional buttons.
23872  * 
23873  * 
23874  * NEEDS Extra CSS? 
23875  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23876  */
23877  
23878 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23879 {
23880     
23881     Roo.apply(this, config);
23882     
23883     // default disabled, based on 'good practice'..
23884     this.disable = this.disable || {};
23885     Roo.applyIf(this.disable, {
23886         fontSize : true,
23887         colors : true,
23888         specialElements : true
23889     });
23890     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23891     
23892     this.editor = config.editor;
23893     this.editorcore = config.editor.editorcore;
23894     
23895     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23896     
23897     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23898     // dont call parent... till later.
23899 }
23900 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23901      
23902     bar : true,
23903     
23904     editor : false,
23905     editorcore : false,
23906     
23907     
23908     formats : [
23909         "p" ,  
23910         "h1","h2","h3","h4","h5","h6", 
23911         "pre", "code", 
23912         "abbr", "acronym", "address", "cite", "samp", "var",
23913         'div','span'
23914     ],
23915     
23916     onRender : function(ct, position)
23917     {
23918        // Roo.log("Call onRender: " + this.xtype);
23919         
23920        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23921        Roo.log(this.el);
23922        this.el.dom.style.marginBottom = '0';
23923        var _this = this;
23924        var editorcore = this.editorcore;
23925        var editor= this.editor;
23926        
23927        var children = [];
23928        var btn = function(id,cmd , toggle, handler, html){
23929        
23930             var  event = toggle ? 'toggle' : 'click';
23931        
23932             var a = {
23933                 size : 'sm',
23934                 xtype: 'Button',
23935                 xns: Roo.bootstrap,
23936                 glyphicon : id,
23937                 cmd : id || cmd,
23938                 enableToggle:toggle !== false,
23939                 html : html || '',
23940                 pressed : toggle ? false : null,
23941                 listeners : {}
23942             };
23943             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23944                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23945             };
23946             children.push(a);
23947             return a;
23948        }
23949        
23950     //    var cb_box = function...
23951         
23952         var style = {
23953                 xtype: 'Button',
23954                 size : 'sm',
23955                 xns: Roo.bootstrap,
23956                 glyphicon : 'font',
23957                 //html : 'submit'
23958                 menu : {
23959                     xtype: 'Menu',
23960                     xns: Roo.bootstrap,
23961                     items:  []
23962                 }
23963         };
23964         Roo.each(this.formats, function(f) {
23965             style.menu.items.push({
23966                 xtype :'MenuItem',
23967                 xns: Roo.bootstrap,
23968                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23969                 tagname : f,
23970                 listeners : {
23971                     click : function()
23972                     {
23973                         editorcore.insertTag(this.tagname);
23974                         editor.focus();
23975                     }
23976                 }
23977                 
23978             });
23979         });
23980         children.push(style);   
23981         
23982         btn('bold',false,true);
23983         btn('italic',false,true);
23984         btn('align-left', 'justifyleft',true);
23985         btn('align-center', 'justifycenter',true);
23986         btn('align-right' , 'justifyright',true);
23987         btn('link', false, false, function(btn) {
23988             //Roo.log("create link?");
23989             var url = prompt(this.createLinkText, this.defaultLinkValue);
23990             if(url && url != 'http:/'+'/'){
23991                 this.editorcore.relayCmd('createlink', url);
23992             }
23993         }),
23994         btn('list','insertunorderedlist',true);
23995         btn('pencil', false,true, function(btn){
23996                 Roo.log(this);
23997                 this.toggleSourceEdit(btn.pressed);
23998         });
23999         
24000         if (this.editor.btns.length > 0) {
24001             for (var i = 0; i<this.editor.btns.length; i++) {
24002                 children.push(this.editor.btns[i]);
24003             }
24004         }
24005         
24006         /*
24007         var cog = {
24008                 xtype: 'Button',
24009                 size : 'sm',
24010                 xns: Roo.bootstrap,
24011                 glyphicon : 'cog',
24012                 //html : 'submit'
24013                 menu : {
24014                     xtype: 'Menu',
24015                     xns: Roo.bootstrap,
24016                     items:  []
24017                 }
24018         };
24019         
24020         cog.menu.items.push({
24021             xtype :'MenuItem',
24022             xns: Roo.bootstrap,
24023             html : Clean styles,
24024             tagname : f,
24025             listeners : {
24026                 click : function()
24027                 {
24028                     editorcore.insertTag(this.tagname);
24029                     editor.focus();
24030                 }
24031             }
24032             
24033         });
24034        */
24035         
24036          
24037        this.xtype = 'NavSimplebar';
24038         
24039         for(var i=0;i< children.length;i++) {
24040             
24041             this.buttons.add(this.addxtypeChild(children[i]));
24042             
24043         }
24044         
24045         editor.on('editorevent', this.updateToolbar, this);
24046     },
24047     onBtnClick : function(id)
24048     {
24049        this.editorcore.relayCmd(id);
24050        this.editorcore.focus();
24051     },
24052     
24053     /**
24054      * Protected method that will not generally be called directly. It triggers
24055      * a toolbar update by reading the markup state of the current selection in the editor.
24056      */
24057     updateToolbar: function(){
24058
24059         if(!this.editorcore.activated){
24060             this.editor.onFirstFocus(); // is this neeed?
24061             return;
24062         }
24063
24064         var btns = this.buttons; 
24065         var doc = this.editorcore.doc;
24066         btns.get('bold').setActive(doc.queryCommandState('bold'));
24067         btns.get('italic').setActive(doc.queryCommandState('italic'));
24068         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24069         
24070         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24071         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24072         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24073         
24074         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24075         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24076          /*
24077         
24078         var ans = this.editorcore.getAllAncestors();
24079         if (this.formatCombo) {
24080             
24081             
24082             var store = this.formatCombo.store;
24083             this.formatCombo.setValue("");
24084             for (var i =0; i < ans.length;i++) {
24085                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24086                     // select it..
24087                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24088                     break;
24089                 }
24090             }
24091         }
24092         
24093         
24094         
24095         // hides menus... - so this cant be on a menu...
24096         Roo.bootstrap.MenuMgr.hideAll();
24097         */
24098         Roo.bootstrap.MenuMgr.hideAll();
24099         //this.editorsyncValue();
24100     },
24101     onFirstFocus: function() {
24102         this.buttons.each(function(item){
24103            item.enable();
24104         });
24105     },
24106     toggleSourceEdit : function(sourceEditMode){
24107         
24108           
24109         if(sourceEditMode){
24110             Roo.log("disabling buttons");
24111            this.buttons.each( function(item){
24112                 if(item.cmd != 'pencil'){
24113                     item.disable();
24114                 }
24115             });
24116           
24117         }else{
24118             Roo.log("enabling buttons");
24119             if(this.editorcore.initialized){
24120                 this.buttons.each( function(item){
24121                     item.enable();
24122                 });
24123             }
24124             
24125         }
24126         Roo.log("calling toggole on editor");
24127         // tell the editor that it's been pressed..
24128         this.editor.toggleSourceEdit(sourceEditMode);
24129        
24130     }
24131 });
24132
24133
24134
24135
24136
24137 /**
24138  * @class Roo.bootstrap.Table.AbstractSelectionModel
24139  * @extends Roo.util.Observable
24140  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24141  * implemented by descendant classes.  This class should not be directly instantiated.
24142  * @constructor
24143  */
24144 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24145     this.locked = false;
24146     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24147 };
24148
24149
24150 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24151     /** @ignore Called by the grid automatically. Do not call directly. */
24152     init : function(grid){
24153         this.grid = grid;
24154         this.initEvents();
24155     },
24156
24157     /**
24158      * Locks the selections.
24159      */
24160     lock : function(){
24161         this.locked = true;
24162     },
24163
24164     /**
24165      * Unlocks the selections.
24166      */
24167     unlock : function(){
24168         this.locked = false;
24169     },
24170
24171     /**
24172      * Returns true if the selections are locked.
24173      * @return {Boolean}
24174      */
24175     isLocked : function(){
24176         return this.locked;
24177     }
24178 });
24179 /**
24180  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24181  * @class Roo.bootstrap.Table.RowSelectionModel
24182  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24183  * It supports multiple selections and keyboard selection/navigation. 
24184  * @constructor
24185  * @param {Object} config
24186  */
24187
24188 Roo.bootstrap.Table.RowSelectionModel = function(config){
24189     Roo.apply(this, config);
24190     this.selections = new Roo.util.MixedCollection(false, function(o){
24191         return o.id;
24192     });
24193
24194     this.last = false;
24195     this.lastActive = false;
24196
24197     this.addEvents({
24198         /**
24199              * @event selectionchange
24200              * Fires when the selection changes
24201              * @param {SelectionModel} this
24202              */
24203             "selectionchange" : true,
24204         /**
24205              * @event afterselectionchange
24206              * Fires after the selection changes (eg. by key press or clicking)
24207              * @param {SelectionModel} this
24208              */
24209             "afterselectionchange" : true,
24210         /**
24211              * @event beforerowselect
24212              * Fires when a row is selected being selected, return false to cancel.
24213              * @param {SelectionModel} this
24214              * @param {Number} rowIndex The selected index
24215              * @param {Boolean} keepExisting False if other selections will be cleared
24216              */
24217             "beforerowselect" : true,
24218         /**
24219              * @event rowselect
24220              * Fires when a row is selected.
24221              * @param {SelectionModel} this
24222              * @param {Number} rowIndex The selected index
24223              * @param {Roo.data.Record} r The record
24224              */
24225             "rowselect" : true,
24226         /**
24227              * @event rowdeselect
24228              * Fires when a row is deselected.
24229              * @param {SelectionModel} this
24230              * @param {Number} rowIndex The selected index
24231              */
24232         "rowdeselect" : true
24233     });
24234     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24235     this.locked = false;
24236  };
24237
24238 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24239     /**
24240      * @cfg {Boolean} singleSelect
24241      * True to allow selection of only one row at a time (defaults to false)
24242      */
24243     singleSelect : false,
24244
24245     // private
24246     initEvents : function()
24247     {
24248
24249         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24250         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24251         //}else{ // allow click to work like normal
24252          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24253         //}
24254         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24255         this.grid.on("rowclick", this.handleMouseDown, this);
24256         
24257         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24258             "up" : function(e){
24259                 if(!e.shiftKey){
24260                     this.selectPrevious(e.shiftKey);
24261                 }else if(this.last !== false && this.lastActive !== false){
24262                     var last = this.last;
24263                     this.selectRange(this.last,  this.lastActive-1);
24264                     this.grid.getView().focusRow(this.lastActive);
24265                     if(last !== false){
24266                         this.last = last;
24267                     }
24268                 }else{
24269                     this.selectFirstRow();
24270                 }
24271                 this.fireEvent("afterselectionchange", this);
24272             },
24273             "down" : function(e){
24274                 if(!e.shiftKey){
24275                     this.selectNext(e.shiftKey);
24276                 }else if(this.last !== false && this.lastActive !== false){
24277                     var last = this.last;
24278                     this.selectRange(this.last,  this.lastActive+1);
24279                     this.grid.getView().focusRow(this.lastActive);
24280                     if(last !== false){
24281                         this.last = last;
24282                     }
24283                 }else{
24284                     this.selectFirstRow();
24285                 }
24286                 this.fireEvent("afterselectionchange", this);
24287             },
24288             scope: this
24289         });
24290         this.grid.store.on('load', function(){
24291             this.selections.clear();
24292         },this);
24293         /*
24294         var view = this.grid.view;
24295         view.on("refresh", this.onRefresh, this);
24296         view.on("rowupdated", this.onRowUpdated, this);
24297         view.on("rowremoved", this.onRemove, this);
24298         */
24299     },
24300
24301     // private
24302     onRefresh : function()
24303     {
24304         var ds = this.grid.store, i, v = this.grid.view;
24305         var s = this.selections;
24306         s.each(function(r){
24307             if((i = ds.indexOfId(r.id)) != -1){
24308                 v.onRowSelect(i);
24309             }else{
24310                 s.remove(r);
24311             }
24312         });
24313     },
24314
24315     // private
24316     onRemove : function(v, index, r){
24317         this.selections.remove(r);
24318     },
24319
24320     // private
24321     onRowUpdated : function(v, index, r){
24322         if(this.isSelected(r)){
24323             v.onRowSelect(index);
24324         }
24325     },
24326
24327     /**
24328      * Select records.
24329      * @param {Array} records The records to select
24330      * @param {Boolean} keepExisting (optional) True to keep existing selections
24331      */
24332     selectRecords : function(records, keepExisting)
24333     {
24334         if(!keepExisting){
24335             this.clearSelections();
24336         }
24337             var ds = this.grid.store;
24338         for(var i = 0, len = records.length; i < len; i++){
24339             this.selectRow(ds.indexOf(records[i]), true);
24340         }
24341     },
24342
24343     /**
24344      * Gets the number of selected rows.
24345      * @return {Number}
24346      */
24347     getCount : function(){
24348         return this.selections.length;
24349     },
24350
24351     /**
24352      * Selects the first row in the grid.
24353      */
24354     selectFirstRow : function(){
24355         this.selectRow(0);
24356     },
24357
24358     /**
24359      * Select the last row.
24360      * @param {Boolean} keepExisting (optional) True to keep existing selections
24361      */
24362     selectLastRow : function(keepExisting){
24363         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24364         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24365     },
24366
24367     /**
24368      * Selects the row immediately following the last selected row.
24369      * @param {Boolean} keepExisting (optional) True to keep existing selections
24370      */
24371     selectNext : function(keepExisting)
24372     {
24373             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24374             this.selectRow(this.last+1, keepExisting);
24375             this.grid.getView().focusRow(this.last);
24376         }
24377     },
24378
24379     /**
24380      * Selects the row that precedes the last selected row.
24381      * @param {Boolean} keepExisting (optional) True to keep existing selections
24382      */
24383     selectPrevious : function(keepExisting){
24384         if(this.last){
24385             this.selectRow(this.last-1, keepExisting);
24386             this.grid.getView().focusRow(this.last);
24387         }
24388     },
24389
24390     /**
24391      * Returns the selected records
24392      * @return {Array} Array of selected records
24393      */
24394     getSelections : function(){
24395         return [].concat(this.selections.items);
24396     },
24397
24398     /**
24399      * Returns the first selected record.
24400      * @return {Record}
24401      */
24402     getSelected : function(){
24403         return this.selections.itemAt(0);
24404     },
24405
24406
24407     /**
24408      * Clears all selections.
24409      */
24410     clearSelections : function(fast)
24411     {
24412         if(this.locked) {
24413             return;
24414         }
24415         if(fast !== true){
24416                 var ds = this.grid.store;
24417             var s = this.selections;
24418             s.each(function(r){
24419                 this.deselectRow(ds.indexOfId(r.id));
24420             }, this);
24421             s.clear();
24422         }else{
24423             this.selections.clear();
24424         }
24425         this.last = false;
24426     },
24427
24428
24429     /**
24430      * Selects all rows.
24431      */
24432     selectAll : function(){
24433         if(this.locked) {
24434             return;
24435         }
24436         this.selections.clear();
24437         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24438             this.selectRow(i, true);
24439         }
24440     },
24441
24442     /**
24443      * Returns True if there is a selection.
24444      * @return {Boolean}
24445      */
24446     hasSelection : function(){
24447         return this.selections.length > 0;
24448     },
24449
24450     /**
24451      * Returns True if the specified row is selected.
24452      * @param {Number/Record} record The record or index of the record to check
24453      * @return {Boolean}
24454      */
24455     isSelected : function(index){
24456             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24457         return (r && this.selections.key(r.id) ? true : false);
24458     },
24459
24460     /**
24461      * Returns True if the specified record id is selected.
24462      * @param {String} id The id of record to check
24463      * @return {Boolean}
24464      */
24465     isIdSelected : function(id){
24466         return (this.selections.key(id) ? true : false);
24467     },
24468
24469
24470     // private
24471     handleMouseDBClick : function(e, t){
24472         
24473     },
24474     // private
24475     handleMouseDown : function(e, t)
24476     {
24477             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24478         if(this.isLocked() || rowIndex < 0 ){
24479             return;
24480         };
24481         if(e.shiftKey && this.last !== false){
24482             var last = this.last;
24483             this.selectRange(last, rowIndex, e.ctrlKey);
24484             this.last = last; // reset the last
24485             t.focus();
24486     
24487         }else{
24488             var isSelected = this.isSelected(rowIndex);
24489             //Roo.log("select row:" + rowIndex);
24490             if(isSelected){
24491                 this.deselectRow(rowIndex);
24492             } else {
24493                         this.selectRow(rowIndex, true);
24494             }
24495     
24496             /*
24497                 if(e.button !== 0 && isSelected){
24498                 alert('rowIndex 2: ' + rowIndex);
24499                     view.focusRow(rowIndex);
24500                 }else if(e.ctrlKey && isSelected){
24501                     this.deselectRow(rowIndex);
24502                 }else if(!isSelected){
24503                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24504                     view.focusRow(rowIndex);
24505                 }
24506             */
24507         }
24508         this.fireEvent("afterselectionchange", this);
24509     },
24510     // private
24511     handleDragableRowClick :  function(grid, rowIndex, e) 
24512     {
24513         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24514             this.selectRow(rowIndex, false);
24515             grid.view.focusRow(rowIndex);
24516              this.fireEvent("afterselectionchange", this);
24517         }
24518     },
24519     
24520     /**
24521      * Selects multiple rows.
24522      * @param {Array} rows Array of the indexes of the row to select
24523      * @param {Boolean} keepExisting (optional) True to keep existing selections
24524      */
24525     selectRows : function(rows, keepExisting){
24526         if(!keepExisting){
24527             this.clearSelections();
24528         }
24529         for(var i = 0, len = rows.length; i < len; i++){
24530             this.selectRow(rows[i], true);
24531         }
24532     },
24533
24534     /**
24535      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24536      * @param {Number} startRow The index of the first row in the range
24537      * @param {Number} endRow The index of the last row in the range
24538      * @param {Boolean} keepExisting (optional) True to retain existing selections
24539      */
24540     selectRange : function(startRow, endRow, keepExisting){
24541         if(this.locked) {
24542             return;
24543         }
24544         if(!keepExisting){
24545             this.clearSelections();
24546         }
24547         if(startRow <= endRow){
24548             for(var i = startRow; i <= endRow; i++){
24549                 this.selectRow(i, true);
24550             }
24551         }else{
24552             for(var i = startRow; i >= endRow; i--){
24553                 this.selectRow(i, true);
24554             }
24555         }
24556     },
24557
24558     /**
24559      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24560      * @param {Number} startRow The index of the first row in the range
24561      * @param {Number} endRow The index of the last row in the range
24562      */
24563     deselectRange : function(startRow, endRow, preventViewNotify){
24564         if(this.locked) {
24565             return;
24566         }
24567         for(var i = startRow; i <= endRow; i++){
24568             this.deselectRow(i, preventViewNotify);
24569         }
24570     },
24571
24572     /**
24573      * Selects a row.
24574      * @param {Number} row The index of the row to select
24575      * @param {Boolean} keepExisting (optional) True to keep existing selections
24576      */
24577     selectRow : function(index, keepExisting, preventViewNotify)
24578     {
24579             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24580             return;
24581         }
24582         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24583             if(!keepExisting || this.singleSelect){
24584                 this.clearSelections();
24585             }
24586             
24587             var r = this.grid.store.getAt(index);
24588             //console.log('selectRow - record id :' + r.id);
24589             
24590             this.selections.add(r);
24591             this.last = this.lastActive = index;
24592             if(!preventViewNotify){
24593                 var proxy = new Roo.Element(
24594                                 this.grid.getRowDom(index)
24595                 );
24596                 proxy.addClass('bg-info info');
24597             }
24598             this.fireEvent("rowselect", this, index, r);
24599             this.fireEvent("selectionchange", this);
24600         }
24601     },
24602
24603     /**
24604      * Deselects a row.
24605      * @param {Number} row The index of the row to deselect
24606      */
24607     deselectRow : function(index, preventViewNotify)
24608     {
24609         if(this.locked) {
24610             return;
24611         }
24612         if(this.last == index){
24613             this.last = false;
24614         }
24615         if(this.lastActive == index){
24616             this.lastActive = false;
24617         }
24618         
24619         var r = this.grid.store.getAt(index);
24620         if (!r) {
24621             return;
24622         }
24623         
24624         this.selections.remove(r);
24625         //.console.log('deselectRow - record id :' + r.id);
24626         if(!preventViewNotify){
24627         
24628             var proxy = new Roo.Element(
24629                 this.grid.getRowDom(index)
24630             );
24631             proxy.removeClass('bg-info info');
24632         }
24633         this.fireEvent("rowdeselect", this, index);
24634         this.fireEvent("selectionchange", this);
24635     },
24636
24637     // private
24638     restoreLast : function(){
24639         if(this._last){
24640             this.last = this._last;
24641         }
24642     },
24643
24644     // private
24645     acceptsNav : function(row, col, cm){
24646         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24647     },
24648
24649     // private
24650     onEditorKey : function(field, e){
24651         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24652         if(k == e.TAB){
24653             e.stopEvent();
24654             ed.completeEdit();
24655             if(e.shiftKey){
24656                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24657             }else{
24658                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24659             }
24660         }else if(k == e.ENTER && !e.ctrlKey){
24661             e.stopEvent();
24662             ed.completeEdit();
24663             if(e.shiftKey){
24664                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24665             }else{
24666                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24667             }
24668         }else if(k == e.ESC){
24669             ed.cancelEdit();
24670         }
24671         if(newCell){
24672             g.startEditing(newCell[0], newCell[1]);
24673         }
24674     }
24675 });
24676 /*
24677  * Based on:
24678  * Ext JS Library 1.1.1
24679  * Copyright(c) 2006-2007, Ext JS, LLC.
24680  *
24681  * Originally Released Under LGPL - original licence link has changed is not relivant.
24682  *
24683  * Fork - LGPL
24684  * <script type="text/javascript">
24685  */
24686  
24687 /**
24688  * @class Roo.bootstrap.PagingToolbar
24689  * @extends Roo.bootstrap.NavSimplebar
24690  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24691  * @constructor
24692  * Create a new PagingToolbar
24693  * @param {Object} config The config object
24694  * @param {Roo.data.Store} store
24695  */
24696 Roo.bootstrap.PagingToolbar = function(config)
24697 {
24698     // old args format still supported... - xtype is prefered..
24699         // created from xtype...
24700     
24701     this.ds = config.dataSource;
24702     
24703     if (config.store && !this.ds) {
24704         this.store= Roo.factory(config.store, Roo.data);
24705         this.ds = this.store;
24706         this.ds.xmodule = this.xmodule || false;
24707     }
24708     
24709     this.toolbarItems = [];
24710     if (config.items) {
24711         this.toolbarItems = config.items;
24712     }
24713     
24714     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24715     
24716     this.cursor = 0;
24717     
24718     if (this.ds) { 
24719         this.bind(this.ds);
24720     }
24721     
24722     if (Roo.bootstrap.version == 4) {
24723         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24724     } else {
24725         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24726     }
24727     
24728 };
24729
24730 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24731     /**
24732      * @cfg {Roo.data.Store} dataSource
24733      * The underlying data store providing the paged data
24734      */
24735     /**
24736      * @cfg {String/HTMLElement/Element} container
24737      * container The id or element that will contain the toolbar
24738      */
24739     /**
24740      * @cfg {Boolean} displayInfo
24741      * True to display the displayMsg (defaults to false)
24742      */
24743     /**
24744      * @cfg {Number} pageSize
24745      * The number of records to display per page (defaults to 20)
24746      */
24747     pageSize: 20,
24748     /**
24749      * @cfg {String} displayMsg
24750      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24751      */
24752     displayMsg : 'Displaying {0} - {1} of {2}',
24753     /**
24754      * @cfg {String} emptyMsg
24755      * The message to display when no records are found (defaults to "No data to display")
24756      */
24757     emptyMsg : 'No data to display',
24758     /**
24759      * Customizable piece of the default paging text (defaults to "Page")
24760      * @type String
24761      */
24762     beforePageText : "Page",
24763     /**
24764      * Customizable piece of the default paging text (defaults to "of %0")
24765      * @type String
24766      */
24767     afterPageText : "of {0}",
24768     /**
24769      * Customizable piece of the default paging text (defaults to "First Page")
24770      * @type String
24771      */
24772     firstText : "First Page",
24773     /**
24774      * Customizable piece of the default paging text (defaults to "Previous Page")
24775      * @type String
24776      */
24777     prevText : "Previous Page",
24778     /**
24779      * Customizable piece of the default paging text (defaults to "Next Page")
24780      * @type String
24781      */
24782     nextText : "Next Page",
24783     /**
24784      * Customizable piece of the default paging text (defaults to "Last Page")
24785      * @type String
24786      */
24787     lastText : "Last Page",
24788     /**
24789      * Customizable piece of the default paging text (defaults to "Refresh")
24790      * @type String
24791      */
24792     refreshText : "Refresh",
24793
24794     buttons : false,
24795     // private
24796     onRender : function(ct, position) 
24797     {
24798         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24799         this.navgroup.parentId = this.id;
24800         this.navgroup.onRender(this.el, null);
24801         // add the buttons to the navgroup
24802         
24803         if(this.displayInfo){
24804             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24805             this.displayEl = this.el.select('.x-paging-info', true).first();
24806 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24807 //            this.displayEl = navel.el.select('span',true).first();
24808         }
24809         
24810         var _this = this;
24811         
24812         if(this.buttons){
24813             Roo.each(_this.buttons, function(e){ // this might need to use render????
24814                Roo.factory(e).render(_this.el);
24815             });
24816         }
24817             
24818         Roo.each(_this.toolbarItems, function(e) {
24819             _this.navgroup.addItem(e);
24820         });
24821         
24822         
24823         this.first = this.navgroup.addItem({
24824             tooltip: this.firstText,
24825             cls: "prev btn-outline-secondary",
24826             html : ' <i class="fa fa-step-backward"></i>',
24827             disabled: true,
24828             preventDefault: true,
24829             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24830         });
24831         
24832         this.prev =  this.navgroup.addItem({
24833             tooltip: this.prevText,
24834             cls: "prev btn-outline-secondary",
24835             html : ' <i class="fa fa-backward"></i>',
24836             disabled: true,
24837             preventDefault: true,
24838             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24839         });
24840     //this.addSeparator();
24841         
24842         
24843         var field = this.navgroup.addItem( {
24844             tagtype : 'span',
24845             cls : 'x-paging-position  btn-outline-secondary',
24846              disabled: true,
24847             html : this.beforePageText  +
24848                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24849                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24850          } ); //?? escaped?
24851         
24852         this.field = field.el.select('input', true).first();
24853         this.field.on("keydown", this.onPagingKeydown, this);
24854         this.field.on("focus", function(){this.dom.select();});
24855     
24856     
24857         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24858         //this.field.setHeight(18);
24859         //this.addSeparator();
24860         this.next = this.navgroup.addItem({
24861             tooltip: this.nextText,
24862             cls: "next btn-outline-secondary",
24863             html : ' <i class="fa fa-forward"></i>',
24864             disabled: true,
24865             preventDefault: true,
24866             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24867         });
24868         this.last = this.navgroup.addItem({
24869             tooltip: this.lastText,
24870             html : ' <i class="fa fa-step-forward"></i>',
24871             cls: "next btn-outline-secondary",
24872             disabled: true,
24873             preventDefault: true,
24874             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24875         });
24876     //this.addSeparator();
24877         this.loading = this.navgroup.addItem({
24878             tooltip: this.refreshText,
24879             cls: "btn-outline-secondary",
24880             html : ' <i class="fa fa-refresh"></i>',
24881             preventDefault: true,
24882             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24883         });
24884         
24885     },
24886
24887     // private
24888     updateInfo : function(){
24889         if(this.displayEl){
24890             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24891             var msg = count == 0 ?
24892                 this.emptyMsg :
24893                 String.format(
24894                     this.displayMsg,
24895                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24896                 );
24897             this.displayEl.update(msg);
24898         }
24899     },
24900
24901     // private
24902     onLoad : function(ds, r, o)
24903     {
24904         this.cursor = o.params.start ? o.params.start : 0;
24905         
24906         var d = this.getPageData(),
24907             ap = d.activePage,
24908             ps = d.pages;
24909         
24910         
24911         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24912         this.field.dom.value = ap;
24913         this.first.setDisabled(ap == 1);
24914         this.prev.setDisabled(ap == 1);
24915         this.next.setDisabled(ap == ps);
24916         this.last.setDisabled(ap == ps);
24917         this.loading.enable();
24918         this.updateInfo();
24919     },
24920
24921     // private
24922     getPageData : function(){
24923         var total = this.ds.getTotalCount();
24924         return {
24925             total : total,
24926             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24927             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24928         };
24929     },
24930
24931     // private
24932     onLoadError : function(){
24933         this.loading.enable();
24934     },
24935
24936     // private
24937     onPagingKeydown : function(e){
24938         var k = e.getKey();
24939         var d = this.getPageData();
24940         if(k == e.RETURN){
24941             var v = this.field.dom.value, pageNum;
24942             if(!v || isNaN(pageNum = parseInt(v, 10))){
24943                 this.field.dom.value = d.activePage;
24944                 return;
24945             }
24946             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24947             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24948             e.stopEvent();
24949         }
24950         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))
24951         {
24952           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24953           this.field.dom.value = pageNum;
24954           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24955           e.stopEvent();
24956         }
24957         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24958         {
24959           var v = this.field.dom.value, pageNum; 
24960           var increment = (e.shiftKey) ? 10 : 1;
24961           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24962                 increment *= -1;
24963           }
24964           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24965             this.field.dom.value = d.activePage;
24966             return;
24967           }
24968           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24969           {
24970             this.field.dom.value = parseInt(v, 10) + increment;
24971             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24972             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24973           }
24974           e.stopEvent();
24975         }
24976     },
24977
24978     // private
24979     beforeLoad : function(){
24980         if(this.loading){
24981             this.loading.disable();
24982         }
24983     },
24984
24985     // private
24986     onClick : function(which){
24987         
24988         var ds = this.ds;
24989         if (!ds) {
24990             return;
24991         }
24992         
24993         switch(which){
24994             case "first":
24995                 ds.load({params:{start: 0, limit: this.pageSize}});
24996             break;
24997             case "prev":
24998                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24999             break;
25000             case "next":
25001                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25002             break;
25003             case "last":
25004                 var total = ds.getTotalCount();
25005                 var extra = total % this.pageSize;
25006                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25007                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25008             break;
25009             case "refresh":
25010                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25011             break;
25012         }
25013     },
25014
25015     /**
25016      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25017      * @param {Roo.data.Store} store The data store to unbind
25018      */
25019     unbind : function(ds){
25020         ds.un("beforeload", this.beforeLoad, this);
25021         ds.un("load", this.onLoad, this);
25022         ds.un("loadexception", this.onLoadError, this);
25023         ds.un("remove", this.updateInfo, this);
25024         ds.un("add", this.updateInfo, this);
25025         this.ds = undefined;
25026     },
25027
25028     /**
25029      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25030      * @param {Roo.data.Store} store The data store to bind
25031      */
25032     bind : function(ds){
25033         ds.on("beforeload", this.beforeLoad, this);
25034         ds.on("load", this.onLoad, this);
25035         ds.on("loadexception", this.onLoadError, this);
25036         ds.on("remove", this.updateInfo, this);
25037         ds.on("add", this.updateInfo, this);
25038         this.ds = ds;
25039     }
25040 });/*
25041  * - LGPL
25042  *
25043  * element
25044  * 
25045  */
25046
25047 /**
25048  * @class Roo.bootstrap.MessageBar
25049  * @extends Roo.bootstrap.Component
25050  * Bootstrap MessageBar class
25051  * @cfg {String} html contents of the MessageBar
25052  * @cfg {String} weight (info | success | warning | danger) default info
25053  * @cfg {String} beforeClass insert the bar before the given class
25054  * @cfg {Boolean} closable (true | false) default false
25055  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25056  * 
25057  * @constructor
25058  * Create a new Element
25059  * @param {Object} config The config object
25060  */
25061
25062 Roo.bootstrap.MessageBar = function(config){
25063     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25064 };
25065
25066 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25067     
25068     html: '',
25069     weight: 'info',
25070     closable: false,
25071     fixed: false,
25072     beforeClass: 'bootstrap-sticky-wrap',
25073     
25074     getAutoCreate : function(){
25075         
25076         var cfg = {
25077             tag: 'div',
25078             cls: 'alert alert-dismissable alert-' + this.weight,
25079             cn: [
25080                 {
25081                     tag: 'span',
25082                     cls: 'message',
25083                     html: this.html || ''
25084                 }
25085             ]
25086         };
25087         
25088         if(this.fixed){
25089             cfg.cls += ' alert-messages-fixed';
25090         }
25091         
25092         if(this.closable){
25093             cfg.cn.push({
25094                 tag: 'button',
25095                 cls: 'close',
25096                 html: 'x'
25097             });
25098         }
25099         
25100         return cfg;
25101     },
25102     
25103     onRender : function(ct, position)
25104     {
25105         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25106         
25107         if(!this.el){
25108             var cfg = Roo.apply({},  this.getAutoCreate());
25109             cfg.id = Roo.id();
25110             
25111             if (this.cls) {
25112                 cfg.cls += ' ' + this.cls;
25113             }
25114             if (this.style) {
25115                 cfg.style = this.style;
25116             }
25117             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25118             
25119             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25120         }
25121         
25122         this.el.select('>button.close').on('click', this.hide, this);
25123         
25124     },
25125     
25126     show : function()
25127     {
25128         if (!this.rendered) {
25129             this.render();
25130         }
25131         
25132         this.el.show();
25133         
25134         this.fireEvent('show', this);
25135         
25136     },
25137     
25138     hide : function()
25139     {
25140         if (!this.rendered) {
25141             this.render();
25142         }
25143         
25144         this.el.hide();
25145         
25146         this.fireEvent('hide', this);
25147     },
25148     
25149     update : function()
25150     {
25151 //        var e = this.el.dom.firstChild;
25152 //        
25153 //        if(this.closable){
25154 //            e = e.nextSibling;
25155 //        }
25156 //        
25157 //        e.data = this.html || '';
25158
25159         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25160     }
25161    
25162 });
25163
25164  
25165
25166      /*
25167  * - LGPL
25168  *
25169  * Graph
25170  * 
25171  */
25172
25173
25174 /**
25175  * @class Roo.bootstrap.Graph
25176  * @extends Roo.bootstrap.Component
25177  * Bootstrap Graph class
25178 > Prameters
25179  -sm {number} sm 4
25180  -md {number} md 5
25181  @cfg {String} graphtype  bar | vbar | pie
25182  @cfg {number} g_x coodinator | centre x (pie)
25183  @cfg {number} g_y coodinator | centre y (pie)
25184  @cfg {number} g_r radius (pie)
25185  @cfg {number} g_height height of the chart (respected by all elements in the set)
25186  @cfg {number} g_width width of the chart (respected by all elements in the set)
25187  @cfg {Object} title The title of the chart
25188     
25189  -{Array}  values
25190  -opts (object) options for the chart 
25191      o {
25192      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25193      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25194      o vgutter (number)
25195      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.
25196      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25197      o to
25198      o stretch (boolean)
25199      o }
25200  -opts (object) options for the pie
25201      o{
25202      o cut
25203      o startAngle (number)
25204      o endAngle (number)
25205      } 
25206  *
25207  * @constructor
25208  * Create a new Input
25209  * @param {Object} config The config object
25210  */
25211
25212 Roo.bootstrap.Graph = function(config){
25213     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25214     
25215     this.addEvents({
25216         // img events
25217         /**
25218          * @event click
25219          * The img click event for the img.
25220          * @param {Roo.EventObject} e
25221          */
25222         "click" : true
25223     });
25224 };
25225
25226 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25227     
25228     sm: 4,
25229     md: 5,
25230     graphtype: 'bar',
25231     g_height: 250,
25232     g_width: 400,
25233     g_x: 50,
25234     g_y: 50,
25235     g_r: 30,
25236     opts:{
25237         //g_colors: this.colors,
25238         g_type: 'soft',
25239         g_gutter: '20%'
25240
25241     },
25242     title : false,
25243
25244     getAutoCreate : function(){
25245         
25246         var cfg = {
25247             tag: 'div',
25248             html : null
25249         };
25250         
25251         
25252         return  cfg;
25253     },
25254
25255     onRender : function(ct,position){
25256         
25257         
25258         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25259         
25260         if (typeof(Raphael) == 'undefined') {
25261             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25262             return;
25263         }
25264         
25265         this.raphael = Raphael(this.el.dom);
25266         
25267                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25268                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25269                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25270                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25271                 /*
25272                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25273                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25274                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25275                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25276                 
25277                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25278                 r.barchart(330, 10, 300, 220, data1);
25279                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25280                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25281                 */
25282                 
25283                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25284                 // r.barchart(30, 30, 560, 250,  xdata, {
25285                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25286                 //     axis : "0 0 1 1",
25287                 //     axisxlabels :  xdata
25288                 //     //yvalues : cols,
25289                    
25290                 // });
25291 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25292 //        
25293 //        this.load(null,xdata,{
25294 //                axis : "0 0 1 1",
25295 //                axisxlabels :  xdata
25296 //                });
25297
25298     },
25299
25300     load : function(graphtype,xdata,opts)
25301     {
25302         this.raphael.clear();
25303         if(!graphtype) {
25304             graphtype = this.graphtype;
25305         }
25306         if(!opts){
25307             opts = this.opts;
25308         }
25309         var r = this.raphael,
25310             fin = function () {
25311                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25312             },
25313             fout = function () {
25314                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25315             },
25316             pfin = function() {
25317                 this.sector.stop();
25318                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25319
25320                 if (this.label) {
25321                     this.label[0].stop();
25322                     this.label[0].attr({ r: 7.5 });
25323                     this.label[1].attr({ "font-weight": 800 });
25324                 }
25325             },
25326             pfout = function() {
25327                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25328
25329                 if (this.label) {
25330                     this.label[0].animate({ r: 5 }, 500, "bounce");
25331                     this.label[1].attr({ "font-weight": 400 });
25332                 }
25333             };
25334
25335         switch(graphtype){
25336             case 'bar':
25337                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25338                 break;
25339             case 'hbar':
25340                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25341                 break;
25342             case 'pie':
25343 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25344 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25345 //            
25346                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25347                 
25348                 break;
25349
25350         }
25351         
25352         if(this.title){
25353             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25354         }
25355         
25356     },
25357     
25358     setTitle: function(o)
25359     {
25360         this.title = o;
25361     },
25362     
25363     initEvents: function() {
25364         
25365         if(!this.href){
25366             this.el.on('click', this.onClick, this);
25367         }
25368     },
25369     
25370     onClick : function(e)
25371     {
25372         Roo.log('img onclick');
25373         this.fireEvent('click', this, e);
25374     }
25375    
25376 });
25377
25378  
25379 /*
25380  * - LGPL
25381  *
25382  * numberBox
25383  * 
25384  */
25385 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25386
25387 /**
25388  * @class Roo.bootstrap.dash.NumberBox
25389  * @extends Roo.bootstrap.Component
25390  * Bootstrap NumberBox class
25391  * @cfg {String} headline Box headline
25392  * @cfg {String} content Box content
25393  * @cfg {String} icon Box icon
25394  * @cfg {String} footer Footer text
25395  * @cfg {String} fhref Footer href
25396  * 
25397  * @constructor
25398  * Create a new NumberBox
25399  * @param {Object} config The config object
25400  */
25401
25402
25403 Roo.bootstrap.dash.NumberBox = function(config){
25404     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25405     
25406 };
25407
25408 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25409     
25410     headline : '',
25411     content : '',
25412     icon : '',
25413     footer : '',
25414     fhref : '',
25415     ficon : '',
25416     
25417     getAutoCreate : function(){
25418         
25419         var cfg = {
25420             tag : 'div',
25421             cls : 'small-box ',
25422             cn : [
25423                 {
25424                     tag : 'div',
25425                     cls : 'inner',
25426                     cn :[
25427                         {
25428                             tag : 'h3',
25429                             cls : 'roo-headline',
25430                             html : this.headline
25431                         },
25432                         {
25433                             tag : 'p',
25434                             cls : 'roo-content',
25435                             html : this.content
25436                         }
25437                     ]
25438                 }
25439             ]
25440         };
25441         
25442         if(this.icon){
25443             cfg.cn.push({
25444                 tag : 'div',
25445                 cls : 'icon',
25446                 cn :[
25447                     {
25448                         tag : 'i',
25449                         cls : 'ion ' + this.icon
25450                     }
25451                 ]
25452             });
25453         }
25454         
25455         if(this.footer){
25456             var footer = {
25457                 tag : 'a',
25458                 cls : 'small-box-footer',
25459                 href : this.fhref || '#',
25460                 html : this.footer
25461             };
25462             
25463             cfg.cn.push(footer);
25464             
25465         }
25466         
25467         return  cfg;
25468     },
25469
25470     onRender : function(ct,position){
25471         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25472
25473
25474        
25475                 
25476     },
25477
25478     setHeadline: function (value)
25479     {
25480         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25481     },
25482     
25483     setFooter: function (value, href)
25484     {
25485         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25486         
25487         if(href){
25488             this.el.select('a.small-box-footer',true).first().attr('href', href);
25489         }
25490         
25491     },
25492
25493     setContent: function (value)
25494     {
25495         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25496     },
25497
25498     initEvents: function() 
25499     {   
25500         
25501     }
25502     
25503 });
25504
25505  
25506 /*
25507  * - LGPL
25508  *
25509  * TabBox
25510  * 
25511  */
25512 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25513
25514 /**
25515  * @class Roo.bootstrap.dash.TabBox
25516  * @extends Roo.bootstrap.Component
25517  * Bootstrap TabBox class
25518  * @cfg {String} title Title of the TabBox
25519  * @cfg {String} icon Icon of the TabBox
25520  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25521  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25522  * 
25523  * @constructor
25524  * Create a new TabBox
25525  * @param {Object} config The config object
25526  */
25527
25528
25529 Roo.bootstrap.dash.TabBox = function(config){
25530     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25531     this.addEvents({
25532         // raw events
25533         /**
25534          * @event addpane
25535          * When a pane is added
25536          * @param {Roo.bootstrap.dash.TabPane} pane
25537          */
25538         "addpane" : true,
25539         /**
25540          * @event activatepane
25541          * When a pane is activated
25542          * @param {Roo.bootstrap.dash.TabPane} pane
25543          */
25544         "activatepane" : true
25545         
25546          
25547     });
25548     
25549     this.panes = [];
25550 };
25551
25552 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25553
25554     title : '',
25555     icon : false,
25556     showtabs : true,
25557     tabScrollable : false,
25558     
25559     getChildContainer : function()
25560     {
25561         return this.el.select('.tab-content', true).first();
25562     },
25563     
25564     getAutoCreate : function(){
25565         
25566         var header = {
25567             tag: 'li',
25568             cls: 'pull-left header',
25569             html: this.title,
25570             cn : []
25571         };
25572         
25573         if(this.icon){
25574             header.cn.push({
25575                 tag: 'i',
25576                 cls: 'fa ' + this.icon
25577             });
25578         }
25579         
25580         var h = {
25581             tag: 'ul',
25582             cls: 'nav nav-tabs pull-right',
25583             cn: [
25584                 header
25585             ]
25586         };
25587         
25588         if(this.tabScrollable){
25589             h = {
25590                 tag: 'div',
25591                 cls: 'tab-header',
25592                 cn: [
25593                     {
25594                         tag: 'ul',
25595                         cls: 'nav nav-tabs pull-right',
25596                         cn: [
25597                             header
25598                         ]
25599                     }
25600                 ]
25601             };
25602         }
25603         
25604         var cfg = {
25605             tag: 'div',
25606             cls: 'nav-tabs-custom',
25607             cn: [
25608                 h,
25609                 {
25610                     tag: 'div',
25611                     cls: 'tab-content no-padding',
25612                     cn: []
25613                 }
25614             ]
25615         };
25616
25617         return  cfg;
25618     },
25619     initEvents : function()
25620     {
25621         //Roo.log('add add pane handler');
25622         this.on('addpane', this.onAddPane, this);
25623     },
25624      /**
25625      * Updates the box title
25626      * @param {String} html to set the title to.
25627      */
25628     setTitle : function(value)
25629     {
25630         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25631     },
25632     onAddPane : function(pane)
25633     {
25634         this.panes.push(pane);
25635         //Roo.log('addpane');
25636         //Roo.log(pane);
25637         // tabs are rendere left to right..
25638         if(!this.showtabs){
25639             return;
25640         }
25641         
25642         var ctr = this.el.select('.nav-tabs', true).first();
25643          
25644          
25645         var existing = ctr.select('.nav-tab',true);
25646         var qty = existing.getCount();;
25647         
25648         
25649         var tab = ctr.createChild({
25650             tag : 'li',
25651             cls : 'nav-tab' + (qty ? '' : ' active'),
25652             cn : [
25653                 {
25654                     tag : 'a',
25655                     href:'#',
25656                     html : pane.title
25657                 }
25658             ]
25659         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25660         pane.tab = tab;
25661         
25662         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25663         if (!qty) {
25664             pane.el.addClass('active');
25665         }
25666         
25667                 
25668     },
25669     onTabClick : function(ev,un,ob,pane)
25670     {
25671         //Roo.log('tab - prev default');
25672         ev.preventDefault();
25673         
25674         
25675         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25676         pane.tab.addClass('active');
25677         //Roo.log(pane.title);
25678         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25679         // technically we should have a deactivate event.. but maybe add later.
25680         // and it should not de-activate the selected tab...
25681         this.fireEvent('activatepane', pane);
25682         pane.el.addClass('active');
25683         pane.fireEvent('activate');
25684         
25685         
25686     },
25687     
25688     getActivePane : function()
25689     {
25690         var r = false;
25691         Roo.each(this.panes, function(p) {
25692             if(p.el.hasClass('active')){
25693                 r = p;
25694                 return false;
25695             }
25696             
25697             return;
25698         });
25699         
25700         return r;
25701     }
25702     
25703     
25704 });
25705
25706  
25707 /*
25708  * - LGPL
25709  *
25710  * Tab pane
25711  * 
25712  */
25713 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25714 /**
25715  * @class Roo.bootstrap.TabPane
25716  * @extends Roo.bootstrap.Component
25717  * Bootstrap TabPane class
25718  * @cfg {Boolean} active (false | true) Default false
25719  * @cfg {String} title title of panel
25720
25721  * 
25722  * @constructor
25723  * Create a new TabPane
25724  * @param {Object} config The config object
25725  */
25726
25727 Roo.bootstrap.dash.TabPane = function(config){
25728     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25729     
25730     this.addEvents({
25731         // raw events
25732         /**
25733          * @event activate
25734          * When a pane is activated
25735          * @param {Roo.bootstrap.dash.TabPane} pane
25736          */
25737         "activate" : true
25738          
25739     });
25740 };
25741
25742 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25743     
25744     active : false,
25745     title : '',
25746     
25747     // the tabBox that this is attached to.
25748     tab : false,
25749      
25750     getAutoCreate : function() 
25751     {
25752         var cfg = {
25753             tag: 'div',
25754             cls: 'tab-pane'
25755         };
25756         
25757         if(this.active){
25758             cfg.cls += ' active';
25759         }
25760         
25761         return cfg;
25762     },
25763     initEvents  : function()
25764     {
25765         //Roo.log('trigger add pane handler');
25766         this.parent().fireEvent('addpane', this)
25767     },
25768     
25769      /**
25770      * Updates the tab title 
25771      * @param {String} html to set the title to.
25772      */
25773     setTitle: function(str)
25774     {
25775         if (!this.tab) {
25776             return;
25777         }
25778         this.title = str;
25779         this.tab.select('a', true).first().dom.innerHTML = str;
25780         
25781     }
25782     
25783     
25784     
25785 });
25786
25787  
25788
25789
25790  /*
25791  * - LGPL
25792  *
25793  * menu
25794  * 
25795  */
25796 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25797
25798 /**
25799  * @class Roo.bootstrap.menu.Menu
25800  * @extends Roo.bootstrap.Component
25801  * Bootstrap Menu class - container for Menu
25802  * @cfg {String} html Text of the menu
25803  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25804  * @cfg {String} icon Font awesome icon
25805  * @cfg {String} pos Menu align to (top | bottom) default bottom
25806  * 
25807  * 
25808  * @constructor
25809  * Create a new Menu
25810  * @param {Object} config The config object
25811  */
25812
25813
25814 Roo.bootstrap.menu.Menu = function(config){
25815     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25816     
25817     this.addEvents({
25818         /**
25819          * @event beforeshow
25820          * Fires before this menu is displayed
25821          * @param {Roo.bootstrap.menu.Menu} this
25822          */
25823         beforeshow : true,
25824         /**
25825          * @event beforehide
25826          * Fires before this menu is hidden
25827          * @param {Roo.bootstrap.menu.Menu} this
25828          */
25829         beforehide : true,
25830         /**
25831          * @event show
25832          * Fires after this menu is displayed
25833          * @param {Roo.bootstrap.menu.Menu} this
25834          */
25835         show : true,
25836         /**
25837          * @event hide
25838          * Fires after this menu is hidden
25839          * @param {Roo.bootstrap.menu.Menu} this
25840          */
25841         hide : true,
25842         /**
25843          * @event click
25844          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25845          * @param {Roo.bootstrap.menu.Menu} this
25846          * @param {Roo.EventObject} e
25847          */
25848         click : true
25849     });
25850     
25851 };
25852
25853 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25854     
25855     submenu : false,
25856     html : '',
25857     weight : 'default',
25858     icon : false,
25859     pos : 'bottom',
25860     
25861     
25862     getChildContainer : function() {
25863         if(this.isSubMenu){
25864             return this.el;
25865         }
25866         
25867         return this.el.select('ul.dropdown-menu', true).first();  
25868     },
25869     
25870     getAutoCreate : function()
25871     {
25872         var text = [
25873             {
25874                 tag : 'span',
25875                 cls : 'roo-menu-text',
25876                 html : this.html
25877             }
25878         ];
25879         
25880         if(this.icon){
25881             text.unshift({
25882                 tag : 'i',
25883                 cls : 'fa ' + this.icon
25884             })
25885         }
25886         
25887         
25888         var cfg = {
25889             tag : 'div',
25890             cls : 'btn-group',
25891             cn : [
25892                 {
25893                     tag : 'button',
25894                     cls : 'dropdown-button btn btn-' + this.weight,
25895                     cn : text
25896                 },
25897                 {
25898                     tag : 'button',
25899                     cls : 'dropdown-toggle btn btn-' + this.weight,
25900                     cn : [
25901                         {
25902                             tag : 'span',
25903                             cls : 'caret'
25904                         }
25905                     ]
25906                 },
25907                 {
25908                     tag : 'ul',
25909                     cls : 'dropdown-menu'
25910                 }
25911             ]
25912             
25913         };
25914         
25915         if(this.pos == 'top'){
25916             cfg.cls += ' dropup';
25917         }
25918         
25919         if(this.isSubMenu){
25920             cfg = {
25921                 tag : 'ul',
25922                 cls : 'dropdown-menu'
25923             }
25924         }
25925         
25926         return cfg;
25927     },
25928     
25929     onRender : function(ct, position)
25930     {
25931         this.isSubMenu = ct.hasClass('dropdown-submenu');
25932         
25933         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25934     },
25935     
25936     initEvents : function() 
25937     {
25938         if(this.isSubMenu){
25939             return;
25940         }
25941         
25942         this.hidden = true;
25943         
25944         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25945         this.triggerEl.on('click', this.onTriggerPress, this);
25946         
25947         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25948         this.buttonEl.on('click', this.onClick, this);
25949         
25950     },
25951     
25952     list : function()
25953     {
25954         if(this.isSubMenu){
25955             return this.el;
25956         }
25957         
25958         return this.el.select('ul.dropdown-menu', true).first();
25959     },
25960     
25961     onClick : function(e)
25962     {
25963         this.fireEvent("click", this, e);
25964     },
25965     
25966     onTriggerPress  : function(e)
25967     {   
25968         if (this.isVisible()) {
25969             this.hide();
25970         } else {
25971             this.show();
25972         }
25973     },
25974     
25975     isVisible : function(){
25976         return !this.hidden;
25977     },
25978     
25979     show : function()
25980     {
25981         this.fireEvent("beforeshow", this);
25982         
25983         this.hidden = false;
25984         this.el.addClass('open');
25985         
25986         Roo.get(document).on("mouseup", this.onMouseUp, this);
25987         
25988         this.fireEvent("show", this);
25989         
25990         
25991     },
25992     
25993     hide : function()
25994     {
25995         this.fireEvent("beforehide", this);
25996         
25997         this.hidden = true;
25998         this.el.removeClass('open');
25999         
26000         Roo.get(document).un("mouseup", this.onMouseUp);
26001         
26002         this.fireEvent("hide", this);
26003     },
26004     
26005     onMouseUp : function()
26006     {
26007         this.hide();
26008     }
26009     
26010 });
26011
26012  
26013  /*
26014  * - LGPL
26015  *
26016  * menu item
26017  * 
26018  */
26019 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26020
26021 /**
26022  * @class Roo.bootstrap.menu.Item
26023  * @extends Roo.bootstrap.Component
26024  * Bootstrap MenuItem class
26025  * @cfg {Boolean} submenu (true | false) default false
26026  * @cfg {String} html text of the item
26027  * @cfg {String} href the link
26028  * @cfg {Boolean} disable (true | false) default false
26029  * @cfg {Boolean} preventDefault (true | false) default true
26030  * @cfg {String} icon Font awesome icon
26031  * @cfg {String} pos Submenu align to (left | right) default right 
26032  * 
26033  * 
26034  * @constructor
26035  * Create a new Item
26036  * @param {Object} config The config object
26037  */
26038
26039
26040 Roo.bootstrap.menu.Item = function(config){
26041     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26042     this.addEvents({
26043         /**
26044          * @event mouseover
26045          * Fires when the mouse is hovering over this menu
26046          * @param {Roo.bootstrap.menu.Item} this
26047          * @param {Roo.EventObject} e
26048          */
26049         mouseover : true,
26050         /**
26051          * @event mouseout
26052          * Fires when the mouse exits this menu
26053          * @param {Roo.bootstrap.menu.Item} this
26054          * @param {Roo.EventObject} e
26055          */
26056         mouseout : true,
26057         // raw events
26058         /**
26059          * @event click
26060          * The raw click event for the entire grid.
26061          * @param {Roo.EventObject} e
26062          */
26063         click : true
26064     });
26065 };
26066
26067 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26068     
26069     submenu : false,
26070     href : '',
26071     html : '',
26072     preventDefault: true,
26073     disable : false,
26074     icon : false,
26075     pos : 'right',
26076     
26077     getAutoCreate : function()
26078     {
26079         var text = [
26080             {
26081                 tag : 'span',
26082                 cls : 'roo-menu-item-text',
26083                 html : this.html
26084             }
26085         ];
26086         
26087         if(this.icon){
26088             text.unshift({
26089                 tag : 'i',
26090                 cls : 'fa ' + this.icon
26091             })
26092         }
26093         
26094         var cfg = {
26095             tag : 'li',
26096             cn : [
26097                 {
26098                     tag : 'a',
26099                     href : this.href || '#',
26100                     cn : text
26101                 }
26102             ]
26103         };
26104         
26105         if(this.disable){
26106             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26107         }
26108         
26109         if(this.submenu){
26110             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26111             
26112             if(this.pos == 'left'){
26113                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26114             }
26115         }
26116         
26117         return cfg;
26118     },
26119     
26120     initEvents : function() 
26121     {
26122         this.el.on('mouseover', this.onMouseOver, this);
26123         this.el.on('mouseout', this.onMouseOut, this);
26124         
26125         this.el.select('a', true).first().on('click', this.onClick, this);
26126         
26127     },
26128     
26129     onClick : function(e)
26130     {
26131         if(this.preventDefault){
26132             e.preventDefault();
26133         }
26134         
26135         this.fireEvent("click", this, e);
26136     },
26137     
26138     onMouseOver : function(e)
26139     {
26140         if(this.submenu && this.pos == 'left'){
26141             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26142         }
26143         
26144         this.fireEvent("mouseover", this, e);
26145     },
26146     
26147     onMouseOut : function(e)
26148     {
26149         this.fireEvent("mouseout", this, e);
26150     }
26151 });
26152
26153  
26154
26155  /*
26156  * - LGPL
26157  *
26158  * menu separator
26159  * 
26160  */
26161 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26162
26163 /**
26164  * @class Roo.bootstrap.menu.Separator
26165  * @extends Roo.bootstrap.Component
26166  * Bootstrap Separator class
26167  * 
26168  * @constructor
26169  * Create a new Separator
26170  * @param {Object} config The config object
26171  */
26172
26173
26174 Roo.bootstrap.menu.Separator = function(config){
26175     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26176 };
26177
26178 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26179     
26180     getAutoCreate : function(){
26181         var cfg = {
26182             tag : 'li',
26183             cls: 'divider'
26184         };
26185         
26186         return cfg;
26187     }
26188    
26189 });
26190
26191  
26192
26193  /*
26194  * - LGPL
26195  *
26196  * Tooltip
26197  * 
26198  */
26199
26200 /**
26201  * @class Roo.bootstrap.Tooltip
26202  * Bootstrap Tooltip class
26203  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26204  * to determine which dom element triggers the tooltip.
26205  * 
26206  * It needs to add support for additional attributes like tooltip-position
26207  * 
26208  * @constructor
26209  * Create a new Toolti
26210  * @param {Object} config The config object
26211  */
26212
26213 Roo.bootstrap.Tooltip = function(config){
26214     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26215     
26216     this.alignment = Roo.bootstrap.Tooltip.alignment;
26217     
26218     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26219         this.alignment = config.alignment;
26220     }
26221     
26222 };
26223
26224 Roo.apply(Roo.bootstrap.Tooltip, {
26225     /**
26226      * @function init initialize tooltip monitoring.
26227      * @static
26228      */
26229     currentEl : false,
26230     currentTip : false,
26231     currentRegion : false,
26232     
26233     //  init : delay?
26234     
26235     init : function()
26236     {
26237         Roo.get(document).on('mouseover', this.enter ,this);
26238         Roo.get(document).on('mouseout', this.leave, this);
26239          
26240         
26241         this.currentTip = new Roo.bootstrap.Tooltip();
26242     },
26243     
26244     enter : function(ev)
26245     {
26246         var dom = ev.getTarget();
26247         
26248         //Roo.log(['enter',dom]);
26249         var el = Roo.fly(dom);
26250         if (this.currentEl) {
26251             //Roo.log(dom);
26252             //Roo.log(this.currentEl);
26253             //Roo.log(this.currentEl.contains(dom));
26254             if (this.currentEl == el) {
26255                 return;
26256             }
26257             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26258                 return;
26259             }
26260
26261         }
26262         
26263         if (this.currentTip.el) {
26264             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26265         }    
26266         //Roo.log(ev);
26267         
26268         if(!el || el.dom == document){
26269             return;
26270         }
26271         
26272         var bindEl = el;
26273         
26274         // you can not look for children, as if el is the body.. then everythign is the child..
26275         if (!el.attr('tooltip')) { //
26276             if (!el.select("[tooltip]").elements.length) {
26277                 return;
26278             }
26279             // is the mouse over this child...?
26280             bindEl = el.select("[tooltip]").first();
26281             var xy = ev.getXY();
26282             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26283                 //Roo.log("not in region.");
26284                 return;
26285             }
26286             //Roo.log("child element over..");
26287             
26288         }
26289         this.currentEl = bindEl;
26290         this.currentTip.bind(bindEl);
26291         this.currentRegion = Roo.lib.Region.getRegion(dom);
26292         this.currentTip.enter();
26293         
26294     },
26295     leave : function(ev)
26296     {
26297         var dom = ev.getTarget();
26298         //Roo.log(['leave',dom]);
26299         if (!this.currentEl) {
26300             return;
26301         }
26302         
26303         
26304         if (dom != this.currentEl.dom) {
26305             return;
26306         }
26307         var xy = ev.getXY();
26308         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26309             return;
26310         }
26311         // only activate leave if mouse cursor is outside... bounding box..
26312         
26313         
26314         
26315         
26316         if (this.currentTip) {
26317             this.currentTip.leave();
26318         }
26319         //Roo.log('clear currentEl');
26320         this.currentEl = false;
26321         
26322         
26323     },
26324     alignment : {
26325         'left' : ['r-l', [-2,0], 'right'],
26326         'right' : ['l-r', [2,0], 'left'],
26327         'bottom' : ['t-b', [0,2], 'top'],
26328         'top' : [ 'b-t', [0,-2], 'bottom']
26329     }
26330     
26331 });
26332
26333
26334 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26335     
26336     
26337     bindEl : false,
26338     
26339     delay : null, // can be { show : 300 , hide: 500}
26340     
26341     timeout : null,
26342     
26343     hoverState : null, //???
26344     
26345     placement : 'bottom', 
26346     
26347     alignment : false,
26348     
26349     getAutoCreate : function(){
26350     
26351         var cfg = {
26352            cls : 'tooltip',
26353            role : 'tooltip',
26354            cn : [
26355                 {
26356                     cls : 'tooltip-arrow'
26357                 },
26358                 {
26359                     cls : 'tooltip-inner'
26360                 }
26361            ]
26362         };
26363         
26364         return cfg;
26365     },
26366     bind : function(el)
26367     {
26368         this.bindEl = el;
26369     },
26370       
26371     
26372     enter : function () {
26373        
26374         if (this.timeout != null) {
26375             clearTimeout(this.timeout);
26376         }
26377         
26378         this.hoverState = 'in';
26379          //Roo.log("enter - show");
26380         if (!this.delay || !this.delay.show) {
26381             this.show();
26382             return;
26383         }
26384         var _t = this;
26385         this.timeout = setTimeout(function () {
26386             if (_t.hoverState == 'in') {
26387                 _t.show();
26388             }
26389         }, this.delay.show);
26390     },
26391     leave : function()
26392     {
26393         clearTimeout(this.timeout);
26394     
26395         this.hoverState = 'out';
26396          if (!this.delay || !this.delay.hide) {
26397             this.hide();
26398             return;
26399         }
26400        
26401         var _t = this;
26402         this.timeout = setTimeout(function () {
26403             //Roo.log("leave - timeout");
26404             
26405             if (_t.hoverState == 'out') {
26406                 _t.hide();
26407                 Roo.bootstrap.Tooltip.currentEl = false;
26408             }
26409         }, delay);
26410     },
26411     
26412     show : function (msg)
26413     {
26414         if (!this.el) {
26415             this.render(document.body);
26416         }
26417         // set content.
26418         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26419         
26420         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26421         
26422         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26423         
26424         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26425         
26426         var placement = typeof this.placement == 'function' ?
26427             this.placement.call(this, this.el, on_el) :
26428             this.placement;
26429             
26430         var autoToken = /\s?auto?\s?/i;
26431         var autoPlace = autoToken.test(placement);
26432         if (autoPlace) {
26433             placement = placement.replace(autoToken, '') || 'top';
26434         }
26435         
26436         //this.el.detach()
26437         //this.el.setXY([0,0]);
26438         this.el.show();
26439         //this.el.dom.style.display='block';
26440         
26441         //this.el.appendTo(on_el);
26442         
26443         var p = this.getPosition();
26444         var box = this.el.getBox();
26445         
26446         if (autoPlace) {
26447             // fixme..
26448         }
26449         
26450         var align = this.alignment[placement];
26451         
26452         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26453         
26454         if(placement == 'top' || placement == 'bottom'){
26455             if(xy[0] < 0){
26456                 placement = 'right';
26457             }
26458             
26459             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26460                 placement = 'left';
26461             }
26462             
26463             var scroll = Roo.select('body', true).first().getScroll();
26464             
26465             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26466                 placement = 'top';
26467             }
26468             
26469             align = this.alignment[placement];
26470         }
26471         
26472         this.el.alignTo(this.bindEl, align[0],align[1]);
26473         //var arrow = this.el.select('.arrow',true).first();
26474         //arrow.set(align[2], 
26475         
26476         this.el.addClass(placement);
26477         
26478         this.el.addClass('in fade');
26479         
26480         this.hoverState = null;
26481         
26482         if (this.el.hasClass('fade')) {
26483             // fade it?
26484         }
26485         
26486     },
26487     hide : function()
26488     {
26489          
26490         if (!this.el) {
26491             return;
26492         }
26493         //this.el.setXY([0,0]);
26494         this.el.removeClass('in');
26495         //this.el.hide();
26496         
26497     }
26498     
26499 });
26500  
26501
26502  /*
26503  * - LGPL
26504  *
26505  * Location Picker
26506  * 
26507  */
26508
26509 /**
26510  * @class Roo.bootstrap.LocationPicker
26511  * @extends Roo.bootstrap.Component
26512  * Bootstrap LocationPicker class
26513  * @cfg {Number} latitude Position when init default 0
26514  * @cfg {Number} longitude Position when init default 0
26515  * @cfg {Number} zoom default 15
26516  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26517  * @cfg {Boolean} mapTypeControl default false
26518  * @cfg {Boolean} disableDoubleClickZoom default false
26519  * @cfg {Boolean} scrollwheel default true
26520  * @cfg {Boolean} streetViewControl default false
26521  * @cfg {Number} radius default 0
26522  * @cfg {String} locationName
26523  * @cfg {Boolean} draggable default true
26524  * @cfg {Boolean} enableAutocomplete default false
26525  * @cfg {Boolean} enableReverseGeocode default true
26526  * @cfg {String} markerTitle
26527  * 
26528  * @constructor
26529  * Create a new LocationPicker
26530  * @param {Object} config The config object
26531  */
26532
26533
26534 Roo.bootstrap.LocationPicker = function(config){
26535     
26536     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26537     
26538     this.addEvents({
26539         /**
26540          * @event initial
26541          * Fires when the picker initialized.
26542          * @param {Roo.bootstrap.LocationPicker} this
26543          * @param {Google Location} location
26544          */
26545         initial : true,
26546         /**
26547          * @event positionchanged
26548          * Fires when the picker position changed.
26549          * @param {Roo.bootstrap.LocationPicker} this
26550          * @param {Google Location} location
26551          */
26552         positionchanged : true,
26553         /**
26554          * @event resize
26555          * Fires when the map resize.
26556          * @param {Roo.bootstrap.LocationPicker} this
26557          */
26558         resize : true,
26559         /**
26560          * @event show
26561          * Fires when the map show.
26562          * @param {Roo.bootstrap.LocationPicker} this
26563          */
26564         show : true,
26565         /**
26566          * @event hide
26567          * Fires when the map hide.
26568          * @param {Roo.bootstrap.LocationPicker} this
26569          */
26570         hide : true,
26571         /**
26572          * @event mapClick
26573          * Fires when click the map.
26574          * @param {Roo.bootstrap.LocationPicker} this
26575          * @param {Map event} e
26576          */
26577         mapClick : true,
26578         /**
26579          * @event mapRightClick
26580          * Fires when right click the map.
26581          * @param {Roo.bootstrap.LocationPicker} this
26582          * @param {Map event} e
26583          */
26584         mapRightClick : true,
26585         /**
26586          * @event markerClick
26587          * Fires when click the marker.
26588          * @param {Roo.bootstrap.LocationPicker} this
26589          * @param {Map event} e
26590          */
26591         markerClick : true,
26592         /**
26593          * @event markerRightClick
26594          * Fires when right click the marker.
26595          * @param {Roo.bootstrap.LocationPicker} this
26596          * @param {Map event} e
26597          */
26598         markerRightClick : true,
26599         /**
26600          * @event OverlayViewDraw
26601          * Fires when OverlayView Draw
26602          * @param {Roo.bootstrap.LocationPicker} this
26603          */
26604         OverlayViewDraw : true,
26605         /**
26606          * @event OverlayViewOnAdd
26607          * Fires when OverlayView Draw
26608          * @param {Roo.bootstrap.LocationPicker} this
26609          */
26610         OverlayViewOnAdd : true,
26611         /**
26612          * @event OverlayViewOnRemove
26613          * Fires when OverlayView Draw
26614          * @param {Roo.bootstrap.LocationPicker} this
26615          */
26616         OverlayViewOnRemove : true,
26617         /**
26618          * @event OverlayViewShow
26619          * Fires when OverlayView Draw
26620          * @param {Roo.bootstrap.LocationPicker} this
26621          * @param {Pixel} cpx
26622          */
26623         OverlayViewShow : true,
26624         /**
26625          * @event OverlayViewHide
26626          * Fires when OverlayView Draw
26627          * @param {Roo.bootstrap.LocationPicker} this
26628          */
26629         OverlayViewHide : true,
26630         /**
26631          * @event loadexception
26632          * Fires when load google lib failed.
26633          * @param {Roo.bootstrap.LocationPicker} this
26634          */
26635         loadexception : true
26636     });
26637         
26638 };
26639
26640 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26641     
26642     gMapContext: false,
26643     
26644     latitude: 0,
26645     longitude: 0,
26646     zoom: 15,
26647     mapTypeId: false,
26648     mapTypeControl: false,
26649     disableDoubleClickZoom: false,
26650     scrollwheel: true,
26651     streetViewControl: false,
26652     radius: 0,
26653     locationName: '',
26654     draggable: true,
26655     enableAutocomplete: false,
26656     enableReverseGeocode: true,
26657     markerTitle: '',
26658     
26659     getAutoCreate: function()
26660     {
26661
26662         var cfg = {
26663             tag: 'div',
26664             cls: 'roo-location-picker'
26665         };
26666         
26667         return cfg
26668     },
26669     
26670     initEvents: function(ct, position)
26671     {       
26672         if(!this.el.getWidth() || this.isApplied()){
26673             return;
26674         }
26675         
26676         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26677         
26678         this.initial();
26679     },
26680     
26681     initial: function()
26682     {
26683         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26684             this.fireEvent('loadexception', this);
26685             return;
26686         }
26687         
26688         if(!this.mapTypeId){
26689             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26690         }
26691         
26692         this.gMapContext = this.GMapContext();
26693         
26694         this.initOverlayView();
26695         
26696         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26697         
26698         var _this = this;
26699                 
26700         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26701             _this.setPosition(_this.gMapContext.marker.position);
26702         });
26703         
26704         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26705             _this.fireEvent('mapClick', this, event);
26706             
26707         });
26708
26709         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26710             _this.fireEvent('mapRightClick', this, event);
26711             
26712         });
26713         
26714         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26715             _this.fireEvent('markerClick', this, event);
26716             
26717         });
26718
26719         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26720             _this.fireEvent('markerRightClick', this, event);
26721             
26722         });
26723         
26724         this.setPosition(this.gMapContext.location);
26725         
26726         this.fireEvent('initial', this, this.gMapContext.location);
26727     },
26728     
26729     initOverlayView: function()
26730     {
26731         var _this = this;
26732         
26733         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26734             
26735             draw: function()
26736             {
26737                 _this.fireEvent('OverlayViewDraw', _this);
26738             },
26739             
26740             onAdd: function()
26741             {
26742                 _this.fireEvent('OverlayViewOnAdd', _this);
26743             },
26744             
26745             onRemove: function()
26746             {
26747                 _this.fireEvent('OverlayViewOnRemove', _this);
26748             },
26749             
26750             show: function(cpx)
26751             {
26752                 _this.fireEvent('OverlayViewShow', _this, cpx);
26753             },
26754             
26755             hide: function()
26756             {
26757                 _this.fireEvent('OverlayViewHide', _this);
26758             }
26759             
26760         });
26761     },
26762     
26763     fromLatLngToContainerPixel: function(event)
26764     {
26765         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26766     },
26767     
26768     isApplied: function() 
26769     {
26770         return this.getGmapContext() == false ? false : true;
26771     },
26772     
26773     getGmapContext: function() 
26774     {
26775         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26776     },
26777     
26778     GMapContext: function() 
26779     {
26780         var position = new google.maps.LatLng(this.latitude, this.longitude);
26781         
26782         var _map = new google.maps.Map(this.el.dom, {
26783             center: position,
26784             zoom: this.zoom,
26785             mapTypeId: this.mapTypeId,
26786             mapTypeControl: this.mapTypeControl,
26787             disableDoubleClickZoom: this.disableDoubleClickZoom,
26788             scrollwheel: this.scrollwheel,
26789             streetViewControl: this.streetViewControl,
26790             locationName: this.locationName,
26791             draggable: this.draggable,
26792             enableAutocomplete: this.enableAutocomplete,
26793             enableReverseGeocode: this.enableReverseGeocode
26794         });
26795         
26796         var _marker = new google.maps.Marker({
26797             position: position,
26798             map: _map,
26799             title: this.markerTitle,
26800             draggable: this.draggable
26801         });
26802         
26803         return {
26804             map: _map,
26805             marker: _marker,
26806             circle: null,
26807             location: position,
26808             radius: this.radius,
26809             locationName: this.locationName,
26810             addressComponents: {
26811                 formatted_address: null,
26812                 addressLine1: null,
26813                 addressLine2: null,
26814                 streetName: null,
26815                 streetNumber: null,
26816                 city: null,
26817                 district: null,
26818                 state: null,
26819                 stateOrProvince: null
26820             },
26821             settings: this,
26822             domContainer: this.el.dom,
26823             geodecoder: new google.maps.Geocoder()
26824         };
26825     },
26826     
26827     drawCircle: function(center, radius, options) 
26828     {
26829         if (this.gMapContext.circle != null) {
26830             this.gMapContext.circle.setMap(null);
26831         }
26832         if (radius > 0) {
26833             radius *= 1;
26834             options = Roo.apply({}, options, {
26835                 strokeColor: "#0000FF",
26836                 strokeOpacity: .35,
26837                 strokeWeight: 2,
26838                 fillColor: "#0000FF",
26839                 fillOpacity: .2
26840             });
26841             
26842             options.map = this.gMapContext.map;
26843             options.radius = radius;
26844             options.center = center;
26845             this.gMapContext.circle = new google.maps.Circle(options);
26846             return this.gMapContext.circle;
26847         }
26848         
26849         return null;
26850     },
26851     
26852     setPosition: function(location) 
26853     {
26854         this.gMapContext.location = location;
26855         this.gMapContext.marker.setPosition(location);
26856         this.gMapContext.map.panTo(location);
26857         this.drawCircle(location, this.gMapContext.radius, {});
26858         
26859         var _this = this;
26860         
26861         if (this.gMapContext.settings.enableReverseGeocode) {
26862             this.gMapContext.geodecoder.geocode({
26863                 latLng: this.gMapContext.location
26864             }, function(results, status) {
26865                 
26866                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26867                     _this.gMapContext.locationName = results[0].formatted_address;
26868                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26869                     
26870                     _this.fireEvent('positionchanged', this, location);
26871                 }
26872             });
26873             
26874             return;
26875         }
26876         
26877         this.fireEvent('positionchanged', this, location);
26878     },
26879     
26880     resize: function()
26881     {
26882         google.maps.event.trigger(this.gMapContext.map, "resize");
26883         
26884         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26885         
26886         this.fireEvent('resize', this);
26887     },
26888     
26889     setPositionByLatLng: function(latitude, longitude)
26890     {
26891         this.setPosition(new google.maps.LatLng(latitude, longitude));
26892     },
26893     
26894     getCurrentPosition: function() 
26895     {
26896         return {
26897             latitude: this.gMapContext.location.lat(),
26898             longitude: this.gMapContext.location.lng()
26899         };
26900     },
26901     
26902     getAddressName: function() 
26903     {
26904         return this.gMapContext.locationName;
26905     },
26906     
26907     getAddressComponents: function() 
26908     {
26909         return this.gMapContext.addressComponents;
26910     },
26911     
26912     address_component_from_google_geocode: function(address_components) 
26913     {
26914         var result = {};
26915         
26916         for (var i = 0; i < address_components.length; i++) {
26917             var component = address_components[i];
26918             if (component.types.indexOf("postal_code") >= 0) {
26919                 result.postalCode = component.short_name;
26920             } else if (component.types.indexOf("street_number") >= 0) {
26921                 result.streetNumber = component.short_name;
26922             } else if (component.types.indexOf("route") >= 0) {
26923                 result.streetName = component.short_name;
26924             } else if (component.types.indexOf("neighborhood") >= 0) {
26925                 result.city = component.short_name;
26926             } else if (component.types.indexOf("locality") >= 0) {
26927                 result.city = component.short_name;
26928             } else if (component.types.indexOf("sublocality") >= 0) {
26929                 result.district = component.short_name;
26930             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26931                 result.stateOrProvince = component.short_name;
26932             } else if (component.types.indexOf("country") >= 0) {
26933                 result.country = component.short_name;
26934             }
26935         }
26936         
26937         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26938         result.addressLine2 = "";
26939         return result;
26940     },
26941     
26942     setZoomLevel: function(zoom)
26943     {
26944         this.gMapContext.map.setZoom(zoom);
26945     },
26946     
26947     show: function()
26948     {
26949         if(!this.el){
26950             return;
26951         }
26952         
26953         this.el.show();
26954         
26955         this.resize();
26956         
26957         this.fireEvent('show', this);
26958     },
26959     
26960     hide: function()
26961     {
26962         if(!this.el){
26963             return;
26964         }
26965         
26966         this.el.hide();
26967         
26968         this.fireEvent('hide', this);
26969     }
26970     
26971 });
26972
26973 Roo.apply(Roo.bootstrap.LocationPicker, {
26974     
26975     OverlayView : function(map, options)
26976     {
26977         options = options || {};
26978         
26979         this.setMap(map);
26980     }
26981     
26982     
26983 });/*
26984  * - LGPL
26985  *
26986  * Alert
26987  * 
26988  */
26989
26990 /**
26991  * @class Roo.bootstrap.Alert
26992  * @extends Roo.bootstrap.Component
26993  * Bootstrap Alert class
26994  * @cfg {String} title The title of alert
26995  * @cfg {String} html The content of alert
26996  * @cfg {String} weight (  success | info | warning | danger )
26997  * @cfg {String} faicon font-awesomeicon
26998  * 
26999  * @constructor
27000  * Create a new alert
27001  * @param {Object} config The config object
27002  */
27003
27004
27005 Roo.bootstrap.Alert = function(config){
27006     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27007     
27008 };
27009
27010 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27011     
27012     title: '',
27013     html: '',
27014     weight: false,
27015     faicon: false,
27016     
27017     getAutoCreate : function()
27018     {
27019         
27020         var cfg = {
27021             tag : 'div',
27022             cls : 'alert',
27023             cn : [
27024                 {
27025                     tag : 'i',
27026                     cls : 'roo-alert-icon'
27027                     
27028                 },
27029                 {
27030                     tag : 'b',
27031                     cls : 'roo-alert-title',
27032                     html : this.title
27033                 },
27034                 {
27035                     tag : 'span',
27036                     cls : 'roo-alert-text',
27037                     html : this.html
27038                 }
27039             ]
27040         };
27041         
27042         if(this.faicon){
27043             cfg.cn[0].cls += ' fa ' + this.faicon;
27044         }
27045         
27046         if(this.weight){
27047             cfg.cls += ' alert-' + this.weight;
27048         }
27049         
27050         return cfg;
27051     },
27052     
27053     initEvents: function() 
27054     {
27055         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27056     },
27057     
27058     setTitle : function(str)
27059     {
27060         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27061     },
27062     
27063     setText : function(str)
27064     {
27065         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27066     },
27067     
27068     setWeight : function(weight)
27069     {
27070         if(this.weight){
27071             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27072         }
27073         
27074         this.weight = weight;
27075         
27076         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27077     },
27078     
27079     setIcon : function(icon)
27080     {
27081         if(this.faicon){
27082             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27083         }
27084         
27085         this.faicon = icon;
27086         
27087         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27088     },
27089     
27090     hide: function() 
27091     {
27092         this.el.hide();   
27093     },
27094     
27095     show: function() 
27096     {  
27097         this.el.show();   
27098     }
27099     
27100 });
27101
27102  
27103 /*
27104 * Licence: LGPL
27105 */
27106
27107 /**
27108  * @class Roo.bootstrap.UploadCropbox
27109  * @extends Roo.bootstrap.Component
27110  * Bootstrap UploadCropbox class
27111  * @cfg {String} emptyText show when image has been loaded
27112  * @cfg {String} rotateNotify show when image too small to rotate
27113  * @cfg {Number} errorTimeout default 3000
27114  * @cfg {Number} minWidth default 300
27115  * @cfg {Number} minHeight default 300
27116  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27117  * @cfg {Boolean} isDocument (true|false) default false
27118  * @cfg {String} url action url
27119  * @cfg {String} paramName default 'imageUpload'
27120  * @cfg {String} method default POST
27121  * @cfg {Boolean} loadMask (true|false) default true
27122  * @cfg {Boolean} loadingText default 'Loading...'
27123  * 
27124  * @constructor
27125  * Create a new UploadCropbox
27126  * @param {Object} config The config object
27127  */
27128
27129 Roo.bootstrap.UploadCropbox = function(config){
27130     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27131     
27132     this.addEvents({
27133         /**
27134          * @event beforeselectfile
27135          * Fire before select file
27136          * @param {Roo.bootstrap.UploadCropbox} this
27137          */
27138         "beforeselectfile" : true,
27139         /**
27140          * @event initial
27141          * Fire after initEvent
27142          * @param {Roo.bootstrap.UploadCropbox} this
27143          */
27144         "initial" : true,
27145         /**
27146          * @event crop
27147          * Fire after initEvent
27148          * @param {Roo.bootstrap.UploadCropbox} this
27149          * @param {String} data
27150          */
27151         "crop" : true,
27152         /**
27153          * @event prepare
27154          * Fire when preparing the file data
27155          * @param {Roo.bootstrap.UploadCropbox} this
27156          * @param {Object} file
27157          */
27158         "prepare" : true,
27159         /**
27160          * @event exception
27161          * Fire when get exception
27162          * @param {Roo.bootstrap.UploadCropbox} this
27163          * @param {XMLHttpRequest} xhr
27164          */
27165         "exception" : true,
27166         /**
27167          * @event beforeloadcanvas
27168          * Fire before load the canvas
27169          * @param {Roo.bootstrap.UploadCropbox} this
27170          * @param {String} src
27171          */
27172         "beforeloadcanvas" : true,
27173         /**
27174          * @event trash
27175          * Fire when trash image
27176          * @param {Roo.bootstrap.UploadCropbox} this
27177          */
27178         "trash" : true,
27179         /**
27180          * @event download
27181          * Fire when download the image
27182          * @param {Roo.bootstrap.UploadCropbox} this
27183          */
27184         "download" : true,
27185         /**
27186          * @event footerbuttonclick
27187          * Fire when footerbuttonclick
27188          * @param {Roo.bootstrap.UploadCropbox} this
27189          * @param {String} type
27190          */
27191         "footerbuttonclick" : true,
27192         /**
27193          * @event resize
27194          * Fire when resize
27195          * @param {Roo.bootstrap.UploadCropbox} this
27196          */
27197         "resize" : true,
27198         /**
27199          * @event rotate
27200          * Fire when rotate the image
27201          * @param {Roo.bootstrap.UploadCropbox} this
27202          * @param {String} pos
27203          */
27204         "rotate" : true,
27205         /**
27206          * @event inspect
27207          * Fire when inspect the file
27208          * @param {Roo.bootstrap.UploadCropbox} this
27209          * @param {Object} file
27210          */
27211         "inspect" : true,
27212         /**
27213          * @event upload
27214          * Fire when xhr upload the file
27215          * @param {Roo.bootstrap.UploadCropbox} this
27216          * @param {Object} data
27217          */
27218         "upload" : true,
27219         /**
27220          * @event arrange
27221          * Fire when arrange the file data
27222          * @param {Roo.bootstrap.UploadCropbox} this
27223          * @param {Object} formData
27224          */
27225         "arrange" : true
27226     });
27227     
27228     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27229 };
27230
27231 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27232     
27233     emptyText : 'Click to upload image',
27234     rotateNotify : 'Image is too small to rotate',
27235     errorTimeout : 3000,
27236     scale : 0,
27237     baseScale : 1,
27238     rotate : 0,
27239     dragable : false,
27240     pinching : false,
27241     mouseX : 0,
27242     mouseY : 0,
27243     cropData : false,
27244     minWidth : 300,
27245     minHeight : 300,
27246     file : false,
27247     exif : {},
27248     baseRotate : 1,
27249     cropType : 'image/jpeg',
27250     buttons : false,
27251     canvasLoaded : false,
27252     isDocument : false,
27253     method : 'POST',
27254     paramName : 'imageUpload',
27255     loadMask : true,
27256     loadingText : 'Loading...',
27257     maskEl : false,
27258     
27259     getAutoCreate : function()
27260     {
27261         var cfg = {
27262             tag : 'div',
27263             cls : 'roo-upload-cropbox',
27264             cn : [
27265                 {
27266                     tag : 'input',
27267                     cls : 'roo-upload-cropbox-selector',
27268                     type : 'file'
27269                 },
27270                 {
27271                     tag : 'div',
27272                     cls : 'roo-upload-cropbox-body',
27273                     style : 'cursor:pointer',
27274                     cn : [
27275                         {
27276                             tag : 'div',
27277                             cls : 'roo-upload-cropbox-preview'
27278                         },
27279                         {
27280                             tag : 'div',
27281                             cls : 'roo-upload-cropbox-thumb'
27282                         },
27283                         {
27284                             tag : 'div',
27285                             cls : 'roo-upload-cropbox-empty-notify',
27286                             html : this.emptyText
27287                         },
27288                         {
27289                             tag : 'div',
27290                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27291                             html : this.rotateNotify
27292                         }
27293                     ]
27294                 },
27295                 {
27296                     tag : 'div',
27297                     cls : 'roo-upload-cropbox-footer',
27298                     cn : {
27299                         tag : 'div',
27300                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27301                         cn : []
27302                     }
27303                 }
27304             ]
27305         };
27306         
27307         return cfg;
27308     },
27309     
27310     onRender : function(ct, position)
27311     {
27312         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27313         
27314         if (this.buttons.length) {
27315             
27316             Roo.each(this.buttons, function(bb) {
27317                 
27318                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27319                 
27320                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27321                 
27322             }, this);
27323         }
27324         
27325         if(this.loadMask){
27326             this.maskEl = this.el;
27327         }
27328     },
27329     
27330     initEvents : function()
27331     {
27332         this.urlAPI = (window.createObjectURL && window) || 
27333                                 (window.URL && URL.revokeObjectURL && URL) || 
27334                                 (window.webkitURL && webkitURL);
27335                         
27336         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27337         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27338         
27339         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27340         this.selectorEl.hide();
27341         
27342         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27343         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27344         
27345         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27346         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27347         this.thumbEl.hide();
27348         
27349         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27350         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27351         
27352         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27353         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27354         this.errorEl.hide();
27355         
27356         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27357         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27358         this.footerEl.hide();
27359         
27360         this.setThumbBoxSize();
27361         
27362         this.bind();
27363         
27364         this.resize();
27365         
27366         this.fireEvent('initial', this);
27367     },
27368
27369     bind : function()
27370     {
27371         var _this = this;
27372         
27373         window.addEventListener("resize", function() { _this.resize(); } );
27374         
27375         this.bodyEl.on('click', this.beforeSelectFile, this);
27376         
27377         if(Roo.isTouch){
27378             this.bodyEl.on('touchstart', this.onTouchStart, this);
27379             this.bodyEl.on('touchmove', this.onTouchMove, this);
27380             this.bodyEl.on('touchend', this.onTouchEnd, this);
27381         }
27382         
27383         if(!Roo.isTouch){
27384             this.bodyEl.on('mousedown', this.onMouseDown, this);
27385             this.bodyEl.on('mousemove', this.onMouseMove, this);
27386             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27387             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27388             Roo.get(document).on('mouseup', this.onMouseUp, this);
27389         }
27390         
27391         this.selectorEl.on('change', this.onFileSelected, this);
27392     },
27393     
27394     reset : function()
27395     {    
27396         this.scale = 0;
27397         this.baseScale = 1;
27398         this.rotate = 0;
27399         this.baseRotate = 1;
27400         this.dragable = false;
27401         this.pinching = false;
27402         this.mouseX = 0;
27403         this.mouseY = 0;
27404         this.cropData = false;
27405         this.notifyEl.dom.innerHTML = this.emptyText;
27406         
27407         this.selectorEl.dom.value = '';
27408         
27409     },
27410     
27411     resize : function()
27412     {
27413         if(this.fireEvent('resize', this) != false){
27414             this.setThumbBoxPosition();
27415             this.setCanvasPosition();
27416         }
27417     },
27418     
27419     onFooterButtonClick : function(e, el, o, type)
27420     {
27421         switch (type) {
27422             case 'rotate-left' :
27423                 this.onRotateLeft(e);
27424                 break;
27425             case 'rotate-right' :
27426                 this.onRotateRight(e);
27427                 break;
27428             case 'picture' :
27429                 this.beforeSelectFile(e);
27430                 break;
27431             case 'trash' :
27432                 this.trash(e);
27433                 break;
27434             case 'crop' :
27435                 this.crop(e);
27436                 break;
27437             case 'download' :
27438                 this.download(e);
27439                 break;
27440             default :
27441                 break;
27442         }
27443         
27444         this.fireEvent('footerbuttonclick', this, type);
27445     },
27446     
27447     beforeSelectFile : function(e)
27448     {
27449         e.preventDefault();
27450         
27451         if(this.fireEvent('beforeselectfile', this) != false){
27452             this.selectorEl.dom.click();
27453         }
27454     },
27455     
27456     onFileSelected : function(e)
27457     {
27458         e.preventDefault();
27459         
27460         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27461             return;
27462         }
27463         
27464         var file = this.selectorEl.dom.files[0];
27465         
27466         if(this.fireEvent('inspect', this, file) != false){
27467             this.prepare(file);
27468         }
27469         
27470     },
27471     
27472     trash : function(e)
27473     {
27474         this.fireEvent('trash', this);
27475     },
27476     
27477     download : function(e)
27478     {
27479         this.fireEvent('download', this);
27480     },
27481     
27482     loadCanvas : function(src)
27483     {   
27484         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27485             
27486             this.reset();
27487             
27488             this.imageEl = document.createElement('img');
27489             
27490             var _this = this;
27491             
27492             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27493             
27494             this.imageEl.src = src;
27495         }
27496     },
27497     
27498     onLoadCanvas : function()
27499     {   
27500         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27501         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27502         
27503         this.bodyEl.un('click', this.beforeSelectFile, this);
27504         
27505         this.notifyEl.hide();
27506         this.thumbEl.show();
27507         this.footerEl.show();
27508         
27509         this.baseRotateLevel();
27510         
27511         if(this.isDocument){
27512             this.setThumbBoxSize();
27513         }
27514         
27515         this.setThumbBoxPosition();
27516         
27517         this.baseScaleLevel();
27518         
27519         this.draw();
27520         
27521         this.resize();
27522         
27523         this.canvasLoaded = true;
27524         
27525         if(this.loadMask){
27526             this.maskEl.unmask();
27527         }
27528         
27529     },
27530     
27531     setCanvasPosition : function()
27532     {   
27533         if(!this.canvasEl){
27534             return;
27535         }
27536         
27537         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27538         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27539         
27540         this.previewEl.setLeft(pw);
27541         this.previewEl.setTop(ph);
27542         
27543     },
27544     
27545     onMouseDown : function(e)
27546     {   
27547         e.stopEvent();
27548         
27549         this.dragable = true;
27550         this.pinching = false;
27551         
27552         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27553             this.dragable = false;
27554             return;
27555         }
27556         
27557         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27558         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27559         
27560     },
27561     
27562     onMouseMove : function(e)
27563     {   
27564         e.stopEvent();
27565         
27566         if(!this.canvasLoaded){
27567             return;
27568         }
27569         
27570         if (!this.dragable){
27571             return;
27572         }
27573         
27574         var minX = Math.ceil(this.thumbEl.getLeft(true));
27575         var minY = Math.ceil(this.thumbEl.getTop(true));
27576         
27577         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27578         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27579         
27580         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27581         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27582         
27583         x = x - this.mouseX;
27584         y = y - this.mouseY;
27585         
27586         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27587         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27588         
27589         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27590         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27591         
27592         this.previewEl.setLeft(bgX);
27593         this.previewEl.setTop(bgY);
27594         
27595         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27596         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27597     },
27598     
27599     onMouseUp : function(e)
27600     {   
27601         e.stopEvent();
27602         
27603         this.dragable = false;
27604     },
27605     
27606     onMouseWheel : function(e)
27607     {   
27608         e.stopEvent();
27609         
27610         this.startScale = this.scale;
27611         
27612         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27613         
27614         if(!this.zoomable()){
27615             this.scale = this.startScale;
27616             return;
27617         }
27618         
27619         this.draw();
27620         
27621         return;
27622     },
27623     
27624     zoomable : function()
27625     {
27626         var minScale = this.thumbEl.getWidth() / this.minWidth;
27627         
27628         if(this.minWidth < this.minHeight){
27629             minScale = this.thumbEl.getHeight() / this.minHeight;
27630         }
27631         
27632         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27633         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27634         
27635         if(
27636                 this.isDocument &&
27637                 (this.rotate == 0 || this.rotate == 180) && 
27638                 (
27639                     width > this.imageEl.OriginWidth || 
27640                     height > this.imageEl.OriginHeight ||
27641                     (width < this.minWidth && height < this.minHeight)
27642                 )
27643         ){
27644             return false;
27645         }
27646         
27647         if(
27648                 this.isDocument &&
27649                 (this.rotate == 90 || this.rotate == 270) && 
27650                 (
27651                     width > this.imageEl.OriginWidth || 
27652                     height > this.imageEl.OriginHeight ||
27653                     (width < this.minHeight && height < this.minWidth)
27654                 )
27655         ){
27656             return false;
27657         }
27658         
27659         if(
27660                 !this.isDocument &&
27661                 (this.rotate == 0 || this.rotate == 180) && 
27662                 (
27663                     width < this.minWidth || 
27664                     width > this.imageEl.OriginWidth || 
27665                     height < this.minHeight || 
27666                     height > this.imageEl.OriginHeight
27667                 )
27668         ){
27669             return false;
27670         }
27671         
27672         if(
27673                 !this.isDocument &&
27674                 (this.rotate == 90 || this.rotate == 270) && 
27675                 (
27676                     width < this.minHeight || 
27677                     width > this.imageEl.OriginWidth || 
27678                     height < this.minWidth || 
27679                     height > this.imageEl.OriginHeight
27680                 )
27681         ){
27682             return false;
27683         }
27684         
27685         return true;
27686         
27687     },
27688     
27689     onRotateLeft : function(e)
27690     {   
27691         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27692             
27693             var minScale = this.thumbEl.getWidth() / this.minWidth;
27694             
27695             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27696             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27697             
27698             this.startScale = this.scale;
27699             
27700             while (this.getScaleLevel() < minScale){
27701             
27702                 this.scale = this.scale + 1;
27703                 
27704                 if(!this.zoomable()){
27705                     break;
27706                 }
27707                 
27708                 if(
27709                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27710                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27711                 ){
27712                     continue;
27713                 }
27714                 
27715                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27716
27717                 this.draw();
27718                 
27719                 return;
27720             }
27721             
27722             this.scale = this.startScale;
27723             
27724             this.onRotateFail();
27725             
27726             return false;
27727         }
27728         
27729         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27730
27731         if(this.isDocument){
27732             this.setThumbBoxSize();
27733             this.setThumbBoxPosition();
27734             this.setCanvasPosition();
27735         }
27736         
27737         this.draw();
27738         
27739         this.fireEvent('rotate', this, 'left');
27740         
27741     },
27742     
27743     onRotateRight : function(e)
27744     {
27745         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27746             
27747             var minScale = this.thumbEl.getWidth() / this.minWidth;
27748         
27749             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27750             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27751             
27752             this.startScale = this.scale;
27753             
27754             while (this.getScaleLevel() < minScale){
27755             
27756                 this.scale = this.scale + 1;
27757                 
27758                 if(!this.zoomable()){
27759                     break;
27760                 }
27761                 
27762                 if(
27763                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27764                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27765                 ){
27766                     continue;
27767                 }
27768                 
27769                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27770
27771                 this.draw();
27772                 
27773                 return;
27774             }
27775             
27776             this.scale = this.startScale;
27777             
27778             this.onRotateFail();
27779             
27780             return false;
27781         }
27782         
27783         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27784
27785         if(this.isDocument){
27786             this.setThumbBoxSize();
27787             this.setThumbBoxPosition();
27788             this.setCanvasPosition();
27789         }
27790         
27791         this.draw();
27792         
27793         this.fireEvent('rotate', this, 'right');
27794     },
27795     
27796     onRotateFail : function()
27797     {
27798         this.errorEl.show(true);
27799         
27800         var _this = this;
27801         
27802         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27803     },
27804     
27805     draw : function()
27806     {
27807         this.previewEl.dom.innerHTML = '';
27808         
27809         var canvasEl = document.createElement("canvas");
27810         
27811         var contextEl = canvasEl.getContext("2d");
27812         
27813         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27814         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27815         var center = this.imageEl.OriginWidth / 2;
27816         
27817         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27818             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27819             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27820             center = this.imageEl.OriginHeight / 2;
27821         }
27822         
27823         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27824         
27825         contextEl.translate(center, center);
27826         contextEl.rotate(this.rotate * Math.PI / 180);
27827
27828         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27829         
27830         this.canvasEl = document.createElement("canvas");
27831         
27832         this.contextEl = this.canvasEl.getContext("2d");
27833         
27834         switch (this.rotate) {
27835             case 0 :
27836                 
27837                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27838                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27839                 
27840                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27841                 
27842                 break;
27843             case 90 : 
27844                 
27845                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27846                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27847                 
27848                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27849                     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);
27850                     break;
27851                 }
27852                 
27853                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27854                 
27855                 break;
27856             case 180 :
27857                 
27858                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27859                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27860                 
27861                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27862                     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);
27863                     break;
27864                 }
27865                 
27866                 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);
27867                 
27868                 break;
27869             case 270 :
27870                 
27871                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27872                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27873         
27874                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27875                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27876                     break;
27877                 }
27878                 
27879                 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);
27880                 
27881                 break;
27882             default : 
27883                 break;
27884         }
27885         
27886         this.previewEl.appendChild(this.canvasEl);
27887         
27888         this.setCanvasPosition();
27889     },
27890     
27891     crop : function()
27892     {
27893         if(!this.canvasLoaded){
27894             return;
27895         }
27896         
27897         var imageCanvas = document.createElement("canvas");
27898         
27899         var imageContext = imageCanvas.getContext("2d");
27900         
27901         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27902         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27903         
27904         var center = imageCanvas.width / 2;
27905         
27906         imageContext.translate(center, center);
27907         
27908         imageContext.rotate(this.rotate * Math.PI / 180);
27909         
27910         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27911         
27912         var canvas = document.createElement("canvas");
27913         
27914         var context = canvas.getContext("2d");
27915                 
27916         canvas.width = this.minWidth;
27917         canvas.height = this.minHeight;
27918
27919         switch (this.rotate) {
27920             case 0 :
27921                 
27922                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27923                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27924                 
27925                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27926                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27927                 
27928                 var targetWidth = this.minWidth - 2 * x;
27929                 var targetHeight = this.minHeight - 2 * y;
27930                 
27931                 var scale = 1;
27932                 
27933                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27934                     scale = targetWidth / width;
27935                 }
27936                 
27937                 if(x > 0 && y == 0){
27938                     scale = targetHeight / height;
27939                 }
27940                 
27941                 if(x > 0 && y > 0){
27942                     scale = targetWidth / width;
27943                     
27944                     if(width < height){
27945                         scale = targetHeight / height;
27946                     }
27947                 }
27948                 
27949                 context.scale(scale, scale);
27950                 
27951                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27952                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27953
27954                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27955                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27956
27957                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27958                 
27959                 break;
27960             case 90 : 
27961                 
27962                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27963                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27964                 
27965                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27966                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27967                 
27968                 var targetWidth = this.minWidth - 2 * x;
27969                 var targetHeight = this.minHeight - 2 * y;
27970                 
27971                 var scale = 1;
27972                 
27973                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27974                     scale = targetWidth / width;
27975                 }
27976                 
27977                 if(x > 0 && y == 0){
27978                     scale = targetHeight / height;
27979                 }
27980                 
27981                 if(x > 0 && y > 0){
27982                     scale = targetWidth / width;
27983                     
27984                     if(width < height){
27985                         scale = targetHeight / height;
27986                     }
27987                 }
27988                 
27989                 context.scale(scale, scale);
27990                 
27991                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27992                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27993
27994                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27995                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27996                 
27997                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27998                 
27999                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28000                 
28001                 break;
28002             case 180 :
28003                 
28004                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28005                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28006                 
28007                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28008                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28009                 
28010                 var targetWidth = this.minWidth - 2 * x;
28011                 var targetHeight = this.minHeight - 2 * y;
28012                 
28013                 var scale = 1;
28014                 
28015                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28016                     scale = targetWidth / width;
28017                 }
28018                 
28019                 if(x > 0 && y == 0){
28020                     scale = targetHeight / height;
28021                 }
28022                 
28023                 if(x > 0 && y > 0){
28024                     scale = targetWidth / width;
28025                     
28026                     if(width < height){
28027                         scale = targetHeight / height;
28028                     }
28029                 }
28030                 
28031                 context.scale(scale, scale);
28032                 
28033                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28034                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28035
28036                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28037                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28038
28039                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28040                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28041                 
28042                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28043                 
28044                 break;
28045             case 270 :
28046                 
28047                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28048                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28049                 
28050                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28051                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28052                 
28053                 var targetWidth = this.minWidth - 2 * x;
28054                 var targetHeight = this.minHeight - 2 * y;
28055                 
28056                 var scale = 1;
28057                 
28058                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28059                     scale = targetWidth / width;
28060                 }
28061                 
28062                 if(x > 0 && y == 0){
28063                     scale = targetHeight / height;
28064                 }
28065                 
28066                 if(x > 0 && y > 0){
28067                     scale = targetWidth / width;
28068                     
28069                     if(width < height){
28070                         scale = targetHeight / height;
28071                     }
28072                 }
28073                 
28074                 context.scale(scale, scale);
28075                 
28076                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28077                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28078
28079                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28080                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28081                 
28082                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28083                 
28084                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28085                 
28086                 break;
28087             default : 
28088                 break;
28089         }
28090         
28091         this.cropData = canvas.toDataURL(this.cropType);
28092         
28093         if(this.fireEvent('crop', this, this.cropData) !== false){
28094             this.process(this.file, this.cropData);
28095         }
28096         
28097         return;
28098         
28099     },
28100     
28101     setThumbBoxSize : function()
28102     {
28103         var width, height;
28104         
28105         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28106             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28107             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28108             
28109             this.minWidth = width;
28110             this.minHeight = height;
28111             
28112             if(this.rotate == 90 || this.rotate == 270){
28113                 this.minWidth = height;
28114                 this.minHeight = width;
28115             }
28116         }
28117         
28118         height = 300;
28119         width = Math.ceil(this.minWidth * height / this.minHeight);
28120         
28121         if(this.minWidth > this.minHeight){
28122             width = 300;
28123             height = Math.ceil(this.minHeight * width / this.minWidth);
28124         }
28125         
28126         this.thumbEl.setStyle({
28127             width : width + 'px',
28128             height : height + 'px'
28129         });
28130
28131         return;
28132             
28133     },
28134     
28135     setThumbBoxPosition : function()
28136     {
28137         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28138         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28139         
28140         this.thumbEl.setLeft(x);
28141         this.thumbEl.setTop(y);
28142         
28143     },
28144     
28145     baseRotateLevel : function()
28146     {
28147         this.baseRotate = 1;
28148         
28149         if(
28150                 typeof(this.exif) != 'undefined' &&
28151                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28152                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28153         ){
28154             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28155         }
28156         
28157         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28158         
28159     },
28160     
28161     baseScaleLevel : function()
28162     {
28163         var width, height;
28164         
28165         if(this.isDocument){
28166             
28167             if(this.baseRotate == 6 || this.baseRotate == 8){
28168             
28169                 height = this.thumbEl.getHeight();
28170                 this.baseScale = height / this.imageEl.OriginWidth;
28171
28172                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28173                     width = this.thumbEl.getWidth();
28174                     this.baseScale = width / this.imageEl.OriginHeight;
28175                 }
28176
28177                 return;
28178             }
28179
28180             height = this.thumbEl.getHeight();
28181             this.baseScale = height / this.imageEl.OriginHeight;
28182
28183             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28184                 width = this.thumbEl.getWidth();
28185                 this.baseScale = width / this.imageEl.OriginWidth;
28186             }
28187
28188             return;
28189         }
28190         
28191         if(this.baseRotate == 6 || this.baseRotate == 8){
28192             
28193             width = this.thumbEl.getHeight();
28194             this.baseScale = width / this.imageEl.OriginHeight;
28195             
28196             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28197                 height = this.thumbEl.getWidth();
28198                 this.baseScale = height / this.imageEl.OriginHeight;
28199             }
28200             
28201             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28202                 height = this.thumbEl.getWidth();
28203                 this.baseScale = height / this.imageEl.OriginHeight;
28204                 
28205                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28206                     width = this.thumbEl.getHeight();
28207                     this.baseScale = width / this.imageEl.OriginWidth;
28208                 }
28209             }
28210             
28211             return;
28212         }
28213         
28214         width = this.thumbEl.getWidth();
28215         this.baseScale = width / this.imageEl.OriginWidth;
28216         
28217         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28218             height = this.thumbEl.getHeight();
28219             this.baseScale = height / this.imageEl.OriginHeight;
28220         }
28221         
28222         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28223             
28224             height = this.thumbEl.getHeight();
28225             this.baseScale = height / this.imageEl.OriginHeight;
28226             
28227             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28228                 width = this.thumbEl.getWidth();
28229                 this.baseScale = width / this.imageEl.OriginWidth;
28230             }
28231             
28232         }
28233         
28234         return;
28235     },
28236     
28237     getScaleLevel : function()
28238     {
28239         return this.baseScale * Math.pow(1.1, this.scale);
28240     },
28241     
28242     onTouchStart : function(e)
28243     {
28244         if(!this.canvasLoaded){
28245             this.beforeSelectFile(e);
28246             return;
28247         }
28248         
28249         var touches = e.browserEvent.touches;
28250         
28251         if(!touches){
28252             return;
28253         }
28254         
28255         if(touches.length == 1){
28256             this.onMouseDown(e);
28257             return;
28258         }
28259         
28260         if(touches.length != 2){
28261             return;
28262         }
28263         
28264         var coords = [];
28265         
28266         for(var i = 0, finger; finger = touches[i]; i++){
28267             coords.push(finger.pageX, finger.pageY);
28268         }
28269         
28270         var x = Math.pow(coords[0] - coords[2], 2);
28271         var y = Math.pow(coords[1] - coords[3], 2);
28272         
28273         this.startDistance = Math.sqrt(x + y);
28274         
28275         this.startScale = this.scale;
28276         
28277         this.pinching = true;
28278         this.dragable = false;
28279         
28280     },
28281     
28282     onTouchMove : function(e)
28283     {
28284         if(!this.pinching && !this.dragable){
28285             return;
28286         }
28287         
28288         var touches = e.browserEvent.touches;
28289         
28290         if(!touches){
28291             return;
28292         }
28293         
28294         if(this.dragable){
28295             this.onMouseMove(e);
28296             return;
28297         }
28298         
28299         var coords = [];
28300         
28301         for(var i = 0, finger; finger = touches[i]; i++){
28302             coords.push(finger.pageX, finger.pageY);
28303         }
28304         
28305         var x = Math.pow(coords[0] - coords[2], 2);
28306         var y = Math.pow(coords[1] - coords[3], 2);
28307         
28308         this.endDistance = Math.sqrt(x + y);
28309         
28310         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28311         
28312         if(!this.zoomable()){
28313             this.scale = this.startScale;
28314             return;
28315         }
28316         
28317         this.draw();
28318         
28319     },
28320     
28321     onTouchEnd : function(e)
28322     {
28323         this.pinching = false;
28324         this.dragable = false;
28325         
28326     },
28327     
28328     process : function(file, crop)
28329     {
28330         if(this.loadMask){
28331             this.maskEl.mask(this.loadingText);
28332         }
28333         
28334         this.xhr = new XMLHttpRequest();
28335         
28336         file.xhr = this.xhr;
28337
28338         this.xhr.open(this.method, this.url, true);
28339         
28340         var headers = {
28341             "Accept": "application/json",
28342             "Cache-Control": "no-cache",
28343             "X-Requested-With": "XMLHttpRequest"
28344         };
28345         
28346         for (var headerName in headers) {
28347             var headerValue = headers[headerName];
28348             if (headerValue) {
28349                 this.xhr.setRequestHeader(headerName, headerValue);
28350             }
28351         }
28352         
28353         var _this = this;
28354         
28355         this.xhr.onload = function()
28356         {
28357             _this.xhrOnLoad(_this.xhr);
28358         }
28359         
28360         this.xhr.onerror = function()
28361         {
28362             _this.xhrOnError(_this.xhr);
28363         }
28364         
28365         var formData = new FormData();
28366
28367         formData.append('returnHTML', 'NO');
28368         
28369         if(crop){
28370             formData.append('crop', crop);
28371         }
28372         
28373         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28374             formData.append(this.paramName, file, file.name);
28375         }
28376         
28377         if(typeof(file.filename) != 'undefined'){
28378             formData.append('filename', file.filename);
28379         }
28380         
28381         if(typeof(file.mimetype) != 'undefined'){
28382             formData.append('mimetype', file.mimetype);
28383         }
28384         
28385         if(this.fireEvent('arrange', this, formData) != false){
28386             this.xhr.send(formData);
28387         };
28388     },
28389     
28390     xhrOnLoad : function(xhr)
28391     {
28392         if(this.loadMask){
28393             this.maskEl.unmask();
28394         }
28395         
28396         if (xhr.readyState !== 4) {
28397             this.fireEvent('exception', this, xhr);
28398             return;
28399         }
28400
28401         var response = Roo.decode(xhr.responseText);
28402         
28403         if(!response.success){
28404             this.fireEvent('exception', this, xhr);
28405             return;
28406         }
28407         
28408         var response = Roo.decode(xhr.responseText);
28409         
28410         this.fireEvent('upload', this, response);
28411         
28412     },
28413     
28414     xhrOnError : function()
28415     {
28416         if(this.loadMask){
28417             this.maskEl.unmask();
28418         }
28419         
28420         Roo.log('xhr on error');
28421         
28422         var response = Roo.decode(xhr.responseText);
28423           
28424         Roo.log(response);
28425         
28426     },
28427     
28428     prepare : function(file)
28429     {   
28430         if(this.loadMask){
28431             this.maskEl.mask(this.loadingText);
28432         }
28433         
28434         this.file = false;
28435         this.exif = {};
28436         
28437         if(typeof(file) === 'string'){
28438             this.loadCanvas(file);
28439             return;
28440         }
28441         
28442         if(!file || !this.urlAPI){
28443             return;
28444         }
28445         
28446         this.file = file;
28447         this.cropType = file.type;
28448         
28449         var _this = this;
28450         
28451         if(this.fireEvent('prepare', this, this.file) != false){
28452             
28453             var reader = new FileReader();
28454             
28455             reader.onload = function (e) {
28456                 if (e.target.error) {
28457                     Roo.log(e.target.error);
28458                     return;
28459                 }
28460                 
28461                 var buffer = e.target.result,
28462                     dataView = new DataView(buffer),
28463                     offset = 2,
28464                     maxOffset = dataView.byteLength - 4,
28465                     markerBytes,
28466                     markerLength;
28467                 
28468                 if (dataView.getUint16(0) === 0xffd8) {
28469                     while (offset < maxOffset) {
28470                         markerBytes = dataView.getUint16(offset);
28471                         
28472                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28473                             markerLength = dataView.getUint16(offset + 2) + 2;
28474                             if (offset + markerLength > dataView.byteLength) {
28475                                 Roo.log('Invalid meta data: Invalid segment size.');
28476                                 break;
28477                             }
28478                             
28479                             if(markerBytes == 0xffe1){
28480                                 _this.parseExifData(
28481                                     dataView,
28482                                     offset,
28483                                     markerLength
28484                                 );
28485                             }
28486                             
28487                             offset += markerLength;
28488                             
28489                             continue;
28490                         }
28491                         
28492                         break;
28493                     }
28494                     
28495                 }
28496                 
28497                 var url = _this.urlAPI.createObjectURL(_this.file);
28498                 
28499                 _this.loadCanvas(url);
28500                 
28501                 return;
28502             }
28503             
28504             reader.readAsArrayBuffer(this.file);
28505             
28506         }
28507         
28508     },
28509     
28510     parseExifData : function(dataView, offset, length)
28511     {
28512         var tiffOffset = offset + 10,
28513             littleEndian,
28514             dirOffset;
28515     
28516         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28517             // No Exif data, might be XMP data instead
28518             return;
28519         }
28520         
28521         // Check for the ASCII code for "Exif" (0x45786966):
28522         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28523             // No Exif data, might be XMP data instead
28524             return;
28525         }
28526         if (tiffOffset + 8 > dataView.byteLength) {
28527             Roo.log('Invalid Exif data: Invalid segment size.');
28528             return;
28529         }
28530         // Check for the two null bytes:
28531         if (dataView.getUint16(offset + 8) !== 0x0000) {
28532             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28533             return;
28534         }
28535         // Check the byte alignment:
28536         switch (dataView.getUint16(tiffOffset)) {
28537         case 0x4949:
28538             littleEndian = true;
28539             break;
28540         case 0x4D4D:
28541             littleEndian = false;
28542             break;
28543         default:
28544             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28545             return;
28546         }
28547         // Check for the TIFF tag marker (0x002A):
28548         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28549             Roo.log('Invalid Exif data: Missing TIFF marker.');
28550             return;
28551         }
28552         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28553         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28554         
28555         this.parseExifTags(
28556             dataView,
28557             tiffOffset,
28558             tiffOffset + dirOffset,
28559             littleEndian
28560         );
28561     },
28562     
28563     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28564     {
28565         var tagsNumber,
28566             dirEndOffset,
28567             i;
28568         if (dirOffset + 6 > dataView.byteLength) {
28569             Roo.log('Invalid Exif data: Invalid directory offset.');
28570             return;
28571         }
28572         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28573         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28574         if (dirEndOffset + 4 > dataView.byteLength) {
28575             Roo.log('Invalid Exif data: Invalid directory size.');
28576             return;
28577         }
28578         for (i = 0; i < tagsNumber; i += 1) {
28579             this.parseExifTag(
28580                 dataView,
28581                 tiffOffset,
28582                 dirOffset + 2 + 12 * i, // tag offset
28583                 littleEndian
28584             );
28585         }
28586         // Return the offset to the next directory:
28587         return dataView.getUint32(dirEndOffset, littleEndian);
28588     },
28589     
28590     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28591     {
28592         var tag = dataView.getUint16(offset, littleEndian);
28593         
28594         this.exif[tag] = this.getExifValue(
28595             dataView,
28596             tiffOffset,
28597             offset,
28598             dataView.getUint16(offset + 2, littleEndian), // tag type
28599             dataView.getUint32(offset + 4, littleEndian), // tag length
28600             littleEndian
28601         );
28602     },
28603     
28604     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28605     {
28606         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28607             tagSize,
28608             dataOffset,
28609             values,
28610             i,
28611             str,
28612             c;
28613     
28614         if (!tagType) {
28615             Roo.log('Invalid Exif data: Invalid tag type.');
28616             return;
28617         }
28618         
28619         tagSize = tagType.size * length;
28620         // Determine if the value is contained in the dataOffset bytes,
28621         // or if the value at the dataOffset is a pointer to the actual data:
28622         dataOffset = tagSize > 4 ?
28623                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28624         if (dataOffset + tagSize > dataView.byteLength) {
28625             Roo.log('Invalid Exif data: Invalid data offset.');
28626             return;
28627         }
28628         if (length === 1) {
28629             return tagType.getValue(dataView, dataOffset, littleEndian);
28630         }
28631         values = [];
28632         for (i = 0; i < length; i += 1) {
28633             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28634         }
28635         
28636         if (tagType.ascii) {
28637             str = '';
28638             // Concatenate the chars:
28639             for (i = 0; i < values.length; i += 1) {
28640                 c = values[i];
28641                 // Ignore the terminating NULL byte(s):
28642                 if (c === '\u0000') {
28643                     break;
28644                 }
28645                 str += c;
28646             }
28647             return str;
28648         }
28649         return values;
28650     }
28651     
28652 });
28653
28654 Roo.apply(Roo.bootstrap.UploadCropbox, {
28655     tags : {
28656         'Orientation': 0x0112
28657     },
28658     
28659     Orientation: {
28660             1: 0, //'top-left',
28661 //            2: 'top-right',
28662             3: 180, //'bottom-right',
28663 //            4: 'bottom-left',
28664 //            5: 'left-top',
28665             6: 90, //'right-top',
28666 //            7: 'right-bottom',
28667             8: 270 //'left-bottom'
28668     },
28669     
28670     exifTagTypes : {
28671         // byte, 8-bit unsigned int:
28672         1: {
28673             getValue: function (dataView, dataOffset) {
28674                 return dataView.getUint8(dataOffset);
28675             },
28676             size: 1
28677         },
28678         // ascii, 8-bit byte:
28679         2: {
28680             getValue: function (dataView, dataOffset) {
28681                 return String.fromCharCode(dataView.getUint8(dataOffset));
28682             },
28683             size: 1,
28684             ascii: true
28685         },
28686         // short, 16 bit int:
28687         3: {
28688             getValue: function (dataView, dataOffset, littleEndian) {
28689                 return dataView.getUint16(dataOffset, littleEndian);
28690             },
28691             size: 2
28692         },
28693         // long, 32 bit int:
28694         4: {
28695             getValue: function (dataView, dataOffset, littleEndian) {
28696                 return dataView.getUint32(dataOffset, littleEndian);
28697             },
28698             size: 4
28699         },
28700         // rational = two long values, first is numerator, second is denominator:
28701         5: {
28702             getValue: function (dataView, dataOffset, littleEndian) {
28703                 return dataView.getUint32(dataOffset, littleEndian) /
28704                     dataView.getUint32(dataOffset + 4, littleEndian);
28705             },
28706             size: 8
28707         },
28708         // slong, 32 bit signed int:
28709         9: {
28710             getValue: function (dataView, dataOffset, littleEndian) {
28711                 return dataView.getInt32(dataOffset, littleEndian);
28712             },
28713             size: 4
28714         },
28715         // srational, two slongs, first is numerator, second is denominator:
28716         10: {
28717             getValue: function (dataView, dataOffset, littleEndian) {
28718                 return dataView.getInt32(dataOffset, littleEndian) /
28719                     dataView.getInt32(dataOffset + 4, littleEndian);
28720             },
28721             size: 8
28722         }
28723     },
28724     
28725     footer : {
28726         STANDARD : [
28727             {
28728                 tag : 'div',
28729                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28730                 action : 'rotate-left',
28731                 cn : [
28732                     {
28733                         tag : 'button',
28734                         cls : 'btn btn-default',
28735                         html : '<i class="fa fa-undo"></i>'
28736                     }
28737                 ]
28738             },
28739             {
28740                 tag : 'div',
28741                 cls : 'btn-group roo-upload-cropbox-picture',
28742                 action : 'picture',
28743                 cn : [
28744                     {
28745                         tag : 'button',
28746                         cls : 'btn btn-default',
28747                         html : '<i class="fa fa-picture-o"></i>'
28748                     }
28749                 ]
28750             },
28751             {
28752                 tag : 'div',
28753                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28754                 action : 'rotate-right',
28755                 cn : [
28756                     {
28757                         tag : 'button',
28758                         cls : 'btn btn-default',
28759                         html : '<i class="fa fa-repeat"></i>'
28760                     }
28761                 ]
28762             }
28763         ],
28764         DOCUMENT : [
28765             {
28766                 tag : 'div',
28767                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28768                 action : 'rotate-left',
28769                 cn : [
28770                     {
28771                         tag : 'button',
28772                         cls : 'btn btn-default',
28773                         html : '<i class="fa fa-undo"></i>'
28774                     }
28775                 ]
28776             },
28777             {
28778                 tag : 'div',
28779                 cls : 'btn-group roo-upload-cropbox-download',
28780                 action : 'download',
28781                 cn : [
28782                     {
28783                         tag : 'button',
28784                         cls : 'btn btn-default',
28785                         html : '<i class="fa fa-download"></i>'
28786                     }
28787                 ]
28788             },
28789             {
28790                 tag : 'div',
28791                 cls : 'btn-group roo-upload-cropbox-crop',
28792                 action : 'crop',
28793                 cn : [
28794                     {
28795                         tag : 'button',
28796                         cls : 'btn btn-default',
28797                         html : '<i class="fa fa-crop"></i>'
28798                     }
28799                 ]
28800             },
28801             {
28802                 tag : 'div',
28803                 cls : 'btn-group roo-upload-cropbox-trash',
28804                 action : 'trash',
28805                 cn : [
28806                     {
28807                         tag : 'button',
28808                         cls : 'btn btn-default',
28809                         html : '<i class="fa fa-trash"></i>'
28810                     }
28811                 ]
28812             },
28813             {
28814                 tag : 'div',
28815                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28816                 action : 'rotate-right',
28817                 cn : [
28818                     {
28819                         tag : 'button',
28820                         cls : 'btn btn-default',
28821                         html : '<i class="fa fa-repeat"></i>'
28822                     }
28823                 ]
28824             }
28825         ],
28826         ROTATOR : [
28827             {
28828                 tag : 'div',
28829                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28830                 action : 'rotate-left',
28831                 cn : [
28832                     {
28833                         tag : 'button',
28834                         cls : 'btn btn-default',
28835                         html : '<i class="fa fa-undo"></i>'
28836                     }
28837                 ]
28838             },
28839             {
28840                 tag : 'div',
28841                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28842                 action : 'rotate-right',
28843                 cn : [
28844                     {
28845                         tag : 'button',
28846                         cls : 'btn btn-default',
28847                         html : '<i class="fa fa-repeat"></i>'
28848                     }
28849                 ]
28850             }
28851         ]
28852     }
28853 });
28854
28855 /*
28856 * Licence: LGPL
28857 */
28858
28859 /**
28860  * @class Roo.bootstrap.DocumentManager
28861  * @extends Roo.bootstrap.Component
28862  * Bootstrap DocumentManager class
28863  * @cfg {String} paramName default 'imageUpload'
28864  * @cfg {String} toolTipName default 'filename'
28865  * @cfg {String} method default POST
28866  * @cfg {String} url action url
28867  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28868  * @cfg {Boolean} multiple multiple upload default true
28869  * @cfg {Number} thumbSize default 300
28870  * @cfg {String} fieldLabel
28871  * @cfg {Number} labelWidth default 4
28872  * @cfg {String} labelAlign (left|top) default left
28873  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28874 * @cfg {Number} labellg set the width of label (1-12)
28875  * @cfg {Number} labelmd set the width of label (1-12)
28876  * @cfg {Number} labelsm set the width of label (1-12)
28877  * @cfg {Number} labelxs set the width of label (1-12)
28878  * 
28879  * @constructor
28880  * Create a new DocumentManager
28881  * @param {Object} config The config object
28882  */
28883
28884 Roo.bootstrap.DocumentManager = function(config){
28885     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28886     
28887     this.files = [];
28888     this.delegates = [];
28889     
28890     this.addEvents({
28891         /**
28892          * @event initial
28893          * Fire when initial the DocumentManager
28894          * @param {Roo.bootstrap.DocumentManager} this
28895          */
28896         "initial" : true,
28897         /**
28898          * @event inspect
28899          * inspect selected file
28900          * @param {Roo.bootstrap.DocumentManager} this
28901          * @param {File} file
28902          */
28903         "inspect" : true,
28904         /**
28905          * @event exception
28906          * Fire when xhr load exception
28907          * @param {Roo.bootstrap.DocumentManager} this
28908          * @param {XMLHttpRequest} xhr
28909          */
28910         "exception" : true,
28911         /**
28912          * @event afterupload
28913          * Fire when xhr load exception
28914          * @param {Roo.bootstrap.DocumentManager} this
28915          * @param {XMLHttpRequest} xhr
28916          */
28917         "afterupload" : true,
28918         /**
28919          * @event prepare
28920          * prepare the form data
28921          * @param {Roo.bootstrap.DocumentManager} this
28922          * @param {Object} formData
28923          */
28924         "prepare" : true,
28925         /**
28926          * @event remove
28927          * Fire when remove the file
28928          * @param {Roo.bootstrap.DocumentManager} this
28929          * @param {Object} file
28930          */
28931         "remove" : true,
28932         /**
28933          * @event refresh
28934          * Fire after refresh the file
28935          * @param {Roo.bootstrap.DocumentManager} this
28936          */
28937         "refresh" : true,
28938         /**
28939          * @event click
28940          * Fire after click the image
28941          * @param {Roo.bootstrap.DocumentManager} this
28942          * @param {Object} file
28943          */
28944         "click" : true,
28945         /**
28946          * @event edit
28947          * Fire when upload a image and editable set to true
28948          * @param {Roo.bootstrap.DocumentManager} this
28949          * @param {Object} file
28950          */
28951         "edit" : true,
28952         /**
28953          * @event beforeselectfile
28954          * Fire before select file
28955          * @param {Roo.bootstrap.DocumentManager} this
28956          */
28957         "beforeselectfile" : true,
28958         /**
28959          * @event process
28960          * Fire before process file
28961          * @param {Roo.bootstrap.DocumentManager} this
28962          * @param {Object} file
28963          */
28964         "process" : true,
28965         /**
28966          * @event previewrendered
28967          * Fire when preview rendered
28968          * @param {Roo.bootstrap.DocumentManager} this
28969          * @param {Object} file
28970          */
28971         "previewrendered" : true,
28972         /**
28973          */
28974         "previewResize" : true
28975         
28976     });
28977 };
28978
28979 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28980     
28981     boxes : 0,
28982     inputName : '',
28983     thumbSize : 300,
28984     multiple : true,
28985     files : false,
28986     method : 'POST',
28987     url : '',
28988     paramName : 'imageUpload',
28989     toolTipName : 'filename',
28990     fieldLabel : '',
28991     labelWidth : 4,
28992     labelAlign : 'left',
28993     editable : true,
28994     delegates : false,
28995     xhr : false, 
28996     
28997     labellg : 0,
28998     labelmd : 0,
28999     labelsm : 0,
29000     labelxs : 0,
29001     
29002     getAutoCreate : function()
29003     {   
29004         var managerWidget = {
29005             tag : 'div',
29006             cls : 'roo-document-manager',
29007             cn : [
29008                 {
29009                     tag : 'input',
29010                     cls : 'roo-document-manager-selector',
29011                     type : 'file'
29012                 },
29013                 {
29014                     tag : 'div',
29015                     cls : 'roo-document-manager-uploader',
29016                     cn : [
29017                         {
29018                             tag : 'div',
29019                             cls : 'roo-document-manager-upload-btn',
29020                             html : '<i class="fa fa-plus"></i>'
29021                         }
29022                     ]
29023                     
29024                 }
29025             ]
29026         };
29027         
29028         var content = [
29029             {
29030                 tag : 'div',
29031                 cls : 'column col-md-12',
29032                 cn : managerWidget
29033             }
29034         ];
29035         
29036         if(this.fieldLabel.length){
29037             
29038             content = [
29039                 {
29040                     tag : 'div',
29041                     cls : 'column col-md-12',
29042                     html : this.fieldLabel
29043                 },
29044                 {
29045                     tag : 'div',
29046                     cls : 'column col-md-12',
29047                     cn : managerWidget
29048                 }
29049             ];
29050
29051             if(this.labelAlign == 'left'){
29052                 content = [
29053                     {
29054                         tag : 'div',
29055                         cls : 'column',
29056                         html : this.fieldLabel
29057                     },
29058                     {
29059                         tag : 'div',
29060                         cls : 'column',
29061                         cn : managerWidget
29062                     }
29063                 ];
29064                 
29065                 if(this.labelWidth > 12){
29066                     content[0].style = "width: " + this.labelWidth + 'px';
29067                 }
29068
29069                 if(this.labelWidth < 13 && this.labelmd == 0){
29070                     this.labelmd = this.labelWidth;
29071                 }
29072
29073                 if(this.labellg > 0){
29074                     content[0].cls += ' col-lg-' + this.labellg;
29075                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29076                 }
29077
29078                 if(this.labelmd > 0){
29079                     content[0].cls += ' col-md-' + this.labelmd;
29080                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29081                 }
29082
29083                 if(this.labelsm > 0){
29084                     content[0].cls += ' col-sm-' + this.labelsm;
29085                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29086                 }
29087
29088                 if(this.labelxs > 0){
29089                     content[0].cls += ' col-xs-' + this.labelxs;
29090                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29091                 }
29092                 
29093             }
29094         }
29095         
29096         var cfg = {
29097             tag : 'div',
29098             cls : 'row clearfix',
29099             cn : content
29100         };
29101         
29102         return cfg;
29103         
29104     },
29105     
29106     initEvents : function()
29107     {
29108         this.managerEl = this.el.select('.roo-document-manager', true).first();
29109         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29110         
29111         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29112         this.selectorEl.hide();
29113         
29114         if(this.multiple){
29115             this.selectorEl.attr('multiple', 'multiple');
29116         }
29117         
29118         this.selectorEl.on('change', this.onFileSelected, this);
29119         
29120         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29121         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29122         
29123         this.uploader.on('click', this.onUploaderClick, this);
29124         
29125         this.renderProgressDialog();
29126         
29127         var _this = this;
29128         
29129         window.addEventListener("resize", function() { _this.refresh(); } );
29130         
29131         this.fireEvent('initial', this);
29132     },
29133     
29134     renderProgressDialog : function()
29135     {
29136         var _this = this;
29137         
29138         this.progressDialog = new Roo.bootstrap.Modal({
29139             cls : 'roo-document-manager-progress-dialog',
29140             allow_close : false,
29141             title : '',
29142             buttons : [
29143                 {
29144                     name  :'cancel',
29145                     weight : 'danger',
29146                     html : 'Cancel'
29147                 }
29148             ], 
29149             listeners : { 
29150                 btnclick : function() {
29151                     _this.uploadCancel();
29152                     this.hide();
29153                 }
29154             }
29155         });
29156          
29157         this.progressDialog.render(Roo.get(document.body));
29158          
29159         this.progress = new Roo.bootstrap.Progress({
29160             cls : 'roo-document-manager-progress',
29161             active : true,
29162             striped : true
29163         });
29164         
29165         this.progress.render(this.progressDialog.getChildContainer());
29166         
29167         this.progressBar = new Roo.bootstrap.ProgressBar({
29168             cls : 'roo-document-manager-progress-bar',
29169             aria_valuenow : 0,
29170             aria_valuemin : 0,
29171             aria_valuemax : 12,
29172             panel : 'success'
29173         });
29174         
29175         this.progressBar.render(this.progress.getChildContainer());
29176     },
29177     
29178     onUploaderClick : function(e)
29179     {
29180         e.preventDefault();
29181      
29182         if(this.fireEvent('beforeselectfile', this) != false){
29183             this.selectorEl.dom.click();
29184         }
29185         
29186     },
29187     
29188     onFileSelected : function(e)
29189     {
29190         e.preventDefault();
29191         
29192         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29193             return;
29194         }
29195         
29196         Roo.each(this.selectorEl.dom.files, function(file){
29197             if(this.fireEvent('inspect', this, file) != false){
29198                 this.files.push(file);
29199             }
29200         }, this);
29201         
29202         this.queue();
29203         
29204     },
29205     
29206     queue : function()
29207     {
29208         this.selectorEl.dom.value = '';
29209         
29210         if(!this.files || !this.files.length){
29211             return;
29212         }
29213         
29214         if(this.boxes > 0 && this.files.length > this.boxes){
29215             this.files = this.files.slice(0, this.boxes);
29216         }
29217         
29218         this.uploader.show();
29219         
29220         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29221             this.uploader.hide();
29222         }
29223         
29224         var _this = this;
29225         
29226         var files = [];
29227         
29228         var docs = [];
29229         
29230         Roo.each(this.files, function(file){
29231             
29232             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29233                 var f = this.renderPreview(file);
29234                 files.push(f);
29235                 return;
29236             }
29237             
29238             if(file.type.indexOf('image') != -1){
29239                 this.delegates.push(
29240                     (function(){
29241                         _this.process(file);
29242                     }).createDelegate(this)
29243                 );
29244         
29245                 return;
29246             }
29247             
29248             docs.push(
29249                 (function(){
29250                     _this.process(file);
29251                 }).createDelegate(this)
29252             );
29253             
29254         }, this);
29255         
29256         this.files = files;
29257         
29258         this.delegates = this.delegates.concat(docs);
29259         
29260         if(!this.delegates.length){
29261             this.refresh();
29262             return;
29263         }
29264         
29265         this.progressBar.aria_valuemax = this.delegates.length;
29266         
29267         this.arrange();
29268         
29269         return;
29270     },
29271     
29272     arrange : function()
29273     {
29274         if(!this.delegates.length){
29275             this.progressDialog.hide();
29276             this.refresh();
29277             return;
29278         }
29279         
29280         var delegate = this.delegates.shift();
29281         
29282         this.progressDialog.show();
29283         
29284         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29285         
29286         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29287         
29288         delegate();
29289     },
29290     
29291     refresh : function()
29292     {
29293         this.uploader.show();
29294         
29295         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29296             this.uploader.hide();
29297         }
29298         
29299         Roo.isTouch ? this.closable(false) : this.closable(true);
29300         
29301         this.fireEvent('refresh', this);
29302     },
29303     
29304     onRemove : function(e, el, o)
29305     {
29306         e.preventDefault();
29307         
29308         this.fireEvent('remove', this, o);
29309         
29310     },
29311     
29312     remove : function(o)
29313     {
29314         var files = [];
29315         
29316         Roo.each(this.files, function(file){
29317             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29318                 files.push(file);
29319                 return;
29320             }
29321
29322             o.target.remove();
29323
29324         }, this);
29325         
29326         this.files = files;
29327         
29328         this.refresh();
29329     },
29330     
29331     clear : function()
29332     {
29333         Roo.each(this.files, function(file){
29334             if(!file.target){
29335                 return;
29336             }
29337             
29338             file.target.remove();
29339
29340         }, this);
29341         
29342         this.files = [];
29343         
29344         this.refresh();
29345     },
29346     
29347     onClick : function(e, el, o)
29348     {
29349         e.preventDefault();
29350         
29351         this.fireEvent('click', this, o);
29352         
29353     },
29354     
29355     closable : function(closable)
29356     {
29357         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29358             
29359             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29360             
29361             if(closable){
29362                 el.show();
29363                 return;
29364             }
29365             
29366             el.hide();
29367             
29368         }, this);
29369     },
29370     
29371     xhrOnLoad : function(xhr)
29372     {
29373         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29374             el.remove();
29375         }, this);
29376         
29377         if (xhr.readyState !== 4) {
29378             this.arrange();
29379             this.fireEvent('exception', this, xhr);
29380             return;
29381         }
29382
29383         var response = Roo.decode(xhr.responseText);
29384         
29385         if(!response.success){
29386             this.arrange();
29387             this.fireEvent('exception', this, xhr);
29388             return;
29389         }
29390         
29391         var file = this.renderPreview(response.data);
29392         
29393         this.files.push(file);
29394         
29395         this.arrange();
29396         
29397         this.fireEvent('afterupload', this, xhr);
29398         
29399     },
29400     
29401     xhrOnError : function(xhr)
29402     {
29403         Roo.log('xhr on error');
29404         
29405         var response = Roo.decode(xhr.responseText);
29406           
29407         Roo.log(response);
29408         
29409         this.arrange();
29410     },
29411     
29412     process : function(file)
29413     {
29414         if(this.fireEvent('process', this, file) !== false){
29415             if(this.editable && file.type.indexOf('image') != -1){
29416                 this.fireEvent('edit', this, file);
29417                 return;
29418             }
29419
29420             this.uploadStart(file, false);
29421
29422             return;
29423         }
29424         
29425     },
29426     
29427     uploadStart : function(file, crop)
29428     {
29429         this.xhr = new XMLHttpRequest();
29430         
29431         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29432             this.arrange();
29433             return;
29434         }
29435         
29436         file.xhr = this.xhr;
29437             
29438         this.managerEl.createChild({
29439             tag : 'div',
29440             cls : 'roo-document-manager-loading',
29441             cn : [
29442                 {
29443                     tag : 'div',
29444                     tooltip : file.name,
29445                     cls : 'roo-document-manager-thumb',
29446                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29447                 }
29448             ]
29449
29450         });
29451
29452         this.xhr.open(this.method, this.url, true);
29453         
29454         var headers = {
29455             "Accept": "application/json",
29456             "Cache-Control": "no-cache",
29457             "X-Requested-With": "XMLHttpRequest"
29458         };
29459         
29460         for (var headerName in headers) {
29461             var headerValue = headers[headerName];
29462             if (headerValue) {
29463                 this.xhr.setRequestHeader(headerName, headerValue);
29464             }
29465         }
29466         
29467         var _this = this;
29468         
29469         this.xhr.onload = function()
29470         {
29471             _this.xhrOnLoad(_this.xhr);
29472         }
29473         
29474         this.xhr.onerror = function()
29475         {
29476             _this.xhrOnError(_this.xhr);
29477         }
29478         
29479         var formData = new FormData();
29480
29481         formData.append('returnHTML', 'NO');
29482         
29483         if(crop){
29484             formData.append('crop', crop);
29485         }
29486         
29487         formData.append(this.paramName, file, file.name);
29488         
29489         var options = {
29490             file : file, 
29491             manually : false
29492         };
29493         
29494         if(this.fireEvent('prepare', this, formData, options) != false){
29495             
29496             if(options.manually){
29497                 return;
29498             }
29499             
29500             this.xhr.send(formData);
29501             return;
29502         };
29503         
29504         this.uploadCancel();
29505     },
29506     
29507     uploadCancel : function()
29508     {
29509         if (this.xhr) {
29510             this.xhr.abort();
29511         }
29512         
29513         this.delegates = [];
29514         
29515         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29516             el.remove();
29517         }, this);
29518         
29519         this.arrange();
29520     },
29521     
29522     renderPreview : function(file)
29523     {
29524         if(typeof(file.target) != 'undefined' && file.target){
29525             return file;
29526         }
29527         
29528         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29529         
29530         var previewEl = this.managerEl.createChild({
29531             tag : 'div',
29532             cls : 'roo-document-manager-preview',
29533             cn : [
29534                 {
29535                     tag : 'div',
29536                     tooltip : file[this.toolTipName],
29537                     cls : 'roo-document-manager-thumb',
29538                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29539                 },
29540                 {
29541                     tag : 'button',
29542                     cls : 'close',
29543                     html : '<i class="fa fa-times-circle"></i>'
29544                 }
29545             ]
29546         });
29547
29548         var close = previewEl.select('button.close', true).first();
29549
29550         close.on('click', this.onRemove, this, file);
29551
29552         file.target = previewEl;
29553
29554         var image = previewEl.select('img', true).first();
29555         
29556         var _this = this;
29557         
29558         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29559         
29560         image.on('click', this.onClick, this, file);
29561         
29562         this.fireEvent('previewrendered', this, file);
29563         
29564         return file;
29565         
29566     },
29567     
29568     onPreviewLoad : function(file, image)
29569     {
29570         if(typeof(file.target) == 'undefined' || !file.target){
29571             return;
29572         }
29573         
29574         var width = image.dom.naturalWidth || image.dom.width;
29575         var height = image.dom.naturalHeight || image.dom.height;
29576         
29577         if(!this.previewResize) {
29578             return;
29579         }
29580         
29581         if(width > height){
29582             file.target.addClass('wide');
29583             return;
29584         }
29585         
29586         file.target.addClass('tall');
29587         return;
29588         
29589     },
29590     
29591     uploadFromSource : function(file, crop)
29592     {
29593         this.xhr = new XMLHttpRequest();
29594         
29595         this.managerEl.createChild({
29596             tag : 'div',
29597             cls : 'roo-document-manager-loading',
29598             cn : [
29599                 {
29600                     tag : 'div',
29601                     tooltip : file.name,
29602                     cls : 'roo-document-manager-thumb',
29603                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29604                 }
29605             ]
29606
29607         });
29608
29609         this.xhr.open(this.method, this.url, true);
29610         
29611         var headers = {
29612             "Accept": "application/json",
29613             "Cache-Control": "no-cache",
29614             "X-Requested-With": "XMLHttpRequest"
29615         };
29616         
29617         for (var headerName in headers) {
29618             var headerValue = headers[headerName];
29619             if (headerValue) {
29620                 this.xhr.setRequestHeader(headerName, headerValue);
29621             }
29622         }
29623         
29624         var _this = this;
29625         
29626         this.xhr.onload = function()
29627         {
29628             _this.xhrOnLoad(_this.xhr);
29629         }
29630         
29631         this.xhr.onerror = function()
29632         {
29633             _this.xhrOnError(_this.xhr);
29634         }
29635         
29636         var formData = new FormData();
29637
29638         formData.append('returnHTML', 'NO');
29639         
29640         formData.append('crop', crop);
29641         
29642         if(typeof(file.filename) != 'undefined'){
29643             formData.append('filename', file.filename);
29644         }
29645         
29646         if(typeof(file.mimetype) != 'undefined'){
29647             formData.append('mimetype', file.mimetype);
29648         }
29649         
29650         Roo.log(formData);
29651         
29652         if(this.fireEvent('prepare', this, formData) != false){
29653             this.xhr.send(formData);
29654         };
29655     }
29656 });
29657
29658 /*
29659 * Licence: LGPL
29660 */
29661
29662 /**
29663  * @class Roo.bootstrap.DocumentViewer
29664  * @extends Roo.bootstrap.Component
29665  * Bootstrap DocumentViewer class
29666  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29667  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29668  * 
29669  * @constructor
29670  * Create a new DocumentViewer
29671  * @param {Object} config The config object
29672  */
29673
29674 Roo.bootstrap.DocumentViewer = function(config){
29675     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29676     
29677     this.addEvents({
29678         /**
29679          * @event initial
29680          * Fire after initEvent
29681          * @param {Roo.bootstrap.DocumentViewer} this
29682          */
29683         "initial" : true,
29684         /**
29685          * @event click
29686          * Fire after click
29687          * @param {Roo.bootstrap.DocumentViewer} this
29688          */
29689         "click" : true,
29690         /**
29691          * @event download
29692          * Fire after download button
29693          * @param {Roo.bootstrap.DocumentViewer} this
29694          */
29695         "download" : true,
29696         /**
29697          * @event trash
29698          * Fire after trash button
29699          * @param {Roo.bootstrap.DocumentViewer} this
29700          */
29701         "trash" : true
29702         
29703     });
29704 };
29705
29706 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29707     
29708     showDownload : true,
29709     
29710     showTrash : true,
29711     
29712     getAutoCreate : function()
29713     {
29714         var cfg = {
29715             tag : 'div',
29716             cls : 'roo-document-viewer',
29717             cn : [
29718                 {
29719                     tag : 'div',
29720                     cls : 'roo-document-viewer-body',
29721                     cn : [
29722                         {
29723                             tag : 'div',
29724                             cls : 'roo-document-viewer-thumb',
29725                             cn : [
29726                                 {
29727                                     tag : 'img',
29728                                     cls : 'roo-document-viewer-image'
29729                                 }
29730                             ]
29731                         }
29732                     ]
29733                 },
29734                 {
29735                     tag : 'div',
29736                     cls : 'roo-document-viewer-footer',
29737                     cn : {
29738                         tag : 'div',
29739                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29740                         cn : [
29741                             {
29742                                 tag : 'div',
29743                                 cls : 'btn-group roo-document-viewer-download',
29744                                 cn : [
29745                                     {
29746                                         tag : 'button',
29747                                         cls : 'btn btn-default',
29748                                         html : '<i class="fa fa-download"></i>'
29749                                     }
29750                                 ]
29751                             },
29752                             {
29753                                 tag : 'div',
29754                                 cls : 'btn-group roo-document-viewer-trash',
29755                                 cn : [
29756                                     {
29757                                         tag : 'button',
29758                                         cls : 'btn btn-default',
29759                                         html : '<i class="fa fa-trash"></i>'
29760                                     }
29761                                 ]
29762                             }
29763                         ]
29764                     }
29765                 }
29766             ]
29767         };
29768         
29769         return cfg;
29770     },
29771     
29772     initEvents : function()
29773     {
29774         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29775         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29776         
29777         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29778         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29779         
29780         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29781         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29782         
29783         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29784         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29785         
29786         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29787         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29788         
29789         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29790         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29791         
29792         this.bodyEl.on('click', this.onClick, this);
29793         this.downloadBtn.on('click', this.onDownload, this);
29794         this.trashBtn.on('click', this.onTrash, this);
29795         
29796         this.downloadBtn.hide();
29797         this.trashBtn.hide();
29798         
29799         if(this.showDownload){
29800             this.downloadBtn.show();
29801         }
29802         
29803         if(this.showTrash){
29804             this.trashBtn.show();
29805         }
29806         
29807         if(!this.showDownload && !this.showTrash) {
29808             this.footerEl.hide();
29809         }
29810         
29811     },
29812     
29813     initial : function()
29814     {
29815         this.fireEvent('initial', this);
29816         
29817     },
29818     
29819     onClick : function(e)
29820     {
29821         e.preventDefault();
29822         
29823         this.fireEvent('click', this);
29824     },
29825     
29826     onDownload : function(e)
29827     {
29828         e.preventDefault();
29829         
29830         this.fireEvent('download', this);
29831     },
29832     
29833     onTrash : function(e)
29834     {
29835         e.preventDefault();
29836         
29837         this.fireEvent('trash', this);
29838     }
29839     
29840 });
29841 /*
29842  * - LGPL
29843  *
29844  * nav progress bar
29845  * 
29846  */
29847
29848 /**
29849  * @class Roo.bootstrap.NavProgressBar
29850  * @extends Roo.bootstrap.Component
29851  * Bootstrap NavProgressBar class
29852  * 
29853  * @constructor
29854  * Create a new nav progress bar
29855  * @param {Object} config The config object
29856  */
29857
29858 Roo.bootstrap.NavProgressBar = function(config){
29859     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29860
29861     this.bullets = this.bullets || [];
29862    
29863 //    Roo.bootstrap.NavProgressBar.register(this);
29864      this.addEvents({
29865         /**
29866              * @event changed
29867              * Fires when the active item changes
29868              * @param {Roo.bootstrap.NavProgressBar} this
29869              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29870              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29871          */
29872         'changed': true
29873      });
29874     
29875 };
29876
29877 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29878     
29879     bullets : [],
29880     barItems : [],
29881     
29882     getAutoCreate : function()
29883     {
29884         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29885         
29886         cfg = {
29887             tag : 'div',
29888             cls : 'roo-navigation-bar-group',
29889             cn : [
29890                 {
29891                     tag : 'div',
29892                     cls : 'roo-navigation-top-bar'
29893                 },
29894                 {
29895                     tag : 'div',
29896                     cls : 'roo-navigation-bullets-bar',
29897                     cn : [
29898                         {
29899                             tag : 'ul',
29900                             cls : 'roo-navigation-bar'
29901                         }
29902                     ]
29903                 },
29904                 
29905                 {
29906                     tag : 'div',
29907                     cls : 'roo-navigation-bottom-bar'
29908                 }
29909             ]
29910             
29911         };
29912         
29913         return cfg;
29914         
29915     },
29916     
29917     initEvents: function() 
29918     {
29919         
29920     },
29921     
29922     onRender : function(ct, position) 
29923     {
29924         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29925         
29926         if(this.bullets.length){
29927             Roo.each(this.bullets, function(b){
29928                this.addItem(b);
29929             }, this);
29930         }
29931         
29932         this.format();
29933         
29934     },
29935     
29936     addItem : function(cfg)
29937     {
29938         var item = new Roo.bootstrap.NavProgressItem(cfg);
29939         
29940         item.parentId = this.id;
29941         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29942         
29943         if(cfg.html){
29944             var top = new Roo.bootstrap.Element({
29945                 tag : 'div',
29946                 cls : 'roo-navigation-bar-text'
29947             });
29948             
29949             var bottom = new Roo.bootstrap.Element({
29950                 tag : 'div',
29951                 cls : 'roo-navigation-bar-text'
29952             });
29953             
29954             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29955             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29956             
29957             var topText = new Roo.bootstrap.Element({
29958                 tag : 'span',
29959                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29960             });
29961             
29962             var bottomText = new Roo.bootstrap.Element({
29963                 tag : 'span',
29964                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29965             });
29966             
29967             topText.onRender(top.el, null);
29968             bottomText.onRender(bottom.el, null);
29969             
29970             item.topEl = top;
29971             item.bottomEl = bottom;
29972         }
29973         
29974         this.barItems.push(item);
29975         
29976         return item;
29977     },
29978     
29979     getActive : function()
29980     {
29981         var active = false;
29982         
29983         Roo.each(this.barItems, function(v){
29984             
29985             if (!v.isActive()) {
29986                 return;
29987             }
29988             
29989             active = v;
29990             return false;
29991             
29992         });
29993         
29994         return active;
29995     },
29996     
29997     setActiveItem : function(item)
29998     {
29999         var prev = false;
30000         
30001         Roo.each(this.barItems, function(v){
30002             if (v.rid == item.rid) {
30003                 return ;
30004             }
30005             
30006             if (v.isActive()) {
30007                 v.setActive(false);
30008                 prev = v;
30009             }
30010         });
30011
30012         item.setActive(true);
30013         
30014         this.fireEvent('changed', this, item, prev);
30015     },
30016     
30017     getBarItem: function(rid)
30018     {
30019         var ret = false;
30020         
30021         Roo.each(this.barItems, function(e) {
30022             if (e.rid != rid) {
30023                 return;
30024             }
30025             
30026             ret =  e;
30027             return false;
30028         });
30029         
30030         return ret;
30031     },
30032     
30033     indexOfItem : function(item)
30034     {
30035         var index = false;
30036         
30037         Roo.each(this.barItems, function(v, i){
30038             
30039             if (v.rid != item.rid) {
30040                 return;
30041             }
30042             
30043             index = i;
30044             return false
30045         });
30046         
30047         return index;
30048     },
30049     
30050     setActiveNext : function()
30051     {
30052         var i = this.indexOfItem(this.getActive());
30053         
30054         if (i > this.barItems.length) {
30055             return;
30056         }
30057         
30058         this.setActiveItem(this.barItems[i+1]);
30059     },
30060     
30061     setActivePrev : function()
30062     {
30063         var i = this.indexOfItem(this.getActive());
30064         
30065         if (i  < 1) {
30066             return;
30067         }
30068         
30069         this.setActiveItem(this.barItems[i-1]);
30070     },
30071     
30072     format : function()
30073     {
30074         if(!this.barItems.length){
30075             return;
30076         }
30077      
30078         var width = 100 / this.barItems.length;
30079         
30080         Roo.each(this.barItems, function(i){
30081             i.el.setStyle('width', width + '%');
30082             i.topEl.el.setStyle('width', width + '%');
30083             i.bottomEl.el.setStyle('width', width + '%');
30084         }, this);
30085         
30086     }
30087     
30088 });
30089 /*
30090  * - LGPL
30091  *
30092  * Nav Progress Item
30093  * 
30094  */
30095
30096 /**
30097  * @class Roo.bootstrap.NavProgressItem
30098  * @extends Roo.bootstrap.Component
30099  * Bootstrap NavProgressItem class
30100  * @cfg {String} rid the reference id
30101  * @cfg {Boolean} active (true|false) Is item active default false
30102  * @cfg {Boolean} disabled (true|false) Is item active default false
30103  * @cfg {String} html
30104  * @cfg {String} position (top|bottom) text position default bottom
30105  * @cfg {String} icon show icon instead of number
30106  * 
30107  * @constructor
30108  * Create a new NavProgressItem
30109  * @param {Object} config The config object
30110  */
30111 Roo.bootstrap.NavProgressItem = function(config){
30112     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30113     this.addEvents({
30114         // raw events
30115         /**
30116          * @event click
30117          * The raw click event for the entire grid.
30118          * @param {Roo.bootstrap.NavProgressItem} this
30119          * @param {Roo.EventObject} e
30120          */
30121         "click" : true
30122     });
30123    
30124 };
30125
30126 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30127     
30128     rid : '',
30129     active : false,
30130     disabled : false,
30131     html : '',
30132     position : 'bottom',
30133     icon : false,
30134     
30135     getAutoCreate : function()
30136     {
30137         var iconCls = 'roo-navigation-bar-item-icon';
30138         
30139         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30140         
30141         var cfg = {
30142             tag: 'li',
30143             cls: 'roo-navigation-bar-item',
30144             cn : [
30145                 {
30146                     tag : 'i',
30147                     cls : iconCls
30148                 }
30149             ]
30150         };
30151         
30152         if(this.active){
30153             cfg.cls += ' active';
30154         }
30155         if(this.disabled){
30156             cfg.cls += ' disabled';
30157         }
30158         
30159         return cfg;
30160     },
30161     
30162     disable : function()
30163     {
30164         this.setDisabled(true);
30165     },
30166     
30167     enable : function()
30168     {
30169         this.setDisabled(false);
30170     },
30171     
30172     initEvents: function() 
30173     {
30174         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30175         
30176         this.iconEl.on('click', this.onClick, this);
30177     },
30178     
30179     onClick : function(e)
30180     {
30181         e.preventDefault();
30182         
30183         if(this.disabled){
30184             return;
30185         }
30186         
30187         if(this.fireEvent('click', this, e) === false){
30188             return;
30189         };
30190         
30191         this.parent().setActiveItem(this);
30192     },
30193     
30194     isActive: function () 
30195     {
30196         return this.active;
30197     },
30198     
30199     setActive : function(state)
30200     {
30201         if(this.active == state){
30202             return;
30203         }
30204         
30205         this.active = state;
30206         
30207         if (state) {
30208             this.el.addClass('active');
30209             return;
30210         }
30211         
30212         this.el.removeClass('active');
30213         
30214         return;
30215     },
30216     
30217     setDisabled : function(state)
30218     {
30219         if(this.disabled == state){
30220             return;
30221         }
30222         
30223         this.disabled = state;
30224         
30225         if (state) {
30226             this.el.addClass('disabled');
30227             return;
30228         }
30229         
30230         this.el.removeClass('disabled');
30231     },
30232     
30233     tooltipEl : function()
30234     {
30235         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30236     }
30237 });
30238  
30239
30240  /*
30241  * - LGPL
30242  *
30243  * FieldLabel
30244  * 
30245  */
30246
30247 /**
30248  * @class Roo.bootstrap.FieldLabel
30249  * @extends Roo.bootstrap.Component
30250  * Bootstrap FieldLabel class
30251  * @cfg {String} html contents of the element
30252  * @cfg {String} tag tag of the element default label
30253  * @cfg {String} cls class of the element
30254  * @cfg {String} target label target 
30255  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30256  * @cfg {String} invalidClass default "text-warning"
30257  * @cfg {String} validClass default "text-success"
30258  * @cfg {String} iconTooltip default "This field is required"
30259  * @cfg {String} indicatorpos (left|right) default left
30260  * 
30261  * @constructor
30262  * Create a new FieldLabel
30263  * @param {Object} config The config object
30264  */
30265
30266 Roo.bootstrap.FieldLabel = function(config){
30267     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30268     
30269     this.addEvents({
30270             /**
30271              * @event invalid
30272              * Fires after the field has been marked as invalid.
30273              * @param {Roo.form.FieldLabel} this
30274              * @param {String} msg The validation message
30275              */
30276             invalid : true,
30277             /**
30278              * @event valid
30279              * Fires after the field has been validated with no errors.
30280              * @param {Roo.form.FieldLabel} this
30281              */
30282             valid : true
30283         });
30284 };
30285
30286 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30287     
30288     tag: 'label',
30289     cls: '',
30290     html: '',
30291     target: '',
30292     allowBlank : true,
30293     invalidClass : 'has-warning',
30294     validClass : 'has-success',
30295     iconTooltip : 'This field is required',
30296     indicatorpos : 'left',
30297     
30298     getAutoCreate : function(){
30299         
30300         var cls = "";
30301         if (!this.allowBlank) {
30302             cls  = "visible";
30303         }
30304         
30305         var cfg = {
30306             tag : this.tag,
30307             cls : 'roo-bootstrap-field-label ' + this.cls,
30308             for : this.target,
30309             cn : [
30310                 {
30311                     tag : 'i',
30312                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30313                     tooltip : this.iconTooltip
30314                 },
30315                 {
30316                     tag : 'span',
30317                     html : this.html
30318                 }
30319             ] 
30320         };
30321         
30322         if(this.indicatorpos == 'right'){
30323             var cfg = {
30324                 tag : this.tag,
30325                 cls : 'roo-bootstrap-field-label ' + this.cls,
30326                 for : this.target,
30327                 cn : [
30328                     {
30329                         tag : 'span',
30330                         html : this.html
30331                     },
30332                     {
30333                         tag : 'i',
30334                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30335                         tooltip : this.iconTooltip
30336                     }
30337                 ] 
30338             };
30339         }
30340         
30341         return cfg;
30342     },
30343     
30344     initEvents: function() 
30345     {
30346         Roo.bootstrap.Element.superclass.initEvents.call(this);
30347         
30348         this.indicator = this.indicatorEl();
30349         
30350         if(this.indicator){
30351             this.indicator.removeClass('visible');
30352             this.indicator.addClass('invisible');
30353         }
30354         
30355         Roo.bootstrap.FieldLabel.register(this);
30356     },
30357     
30358     indicatorEl : function()
30359     {
30360         var indicator = this.el.select('i.roo-required-indicator',true).first();
30361         
30362         if(!indicator){
30363             return false;
30364         }
30365         
30366         return indicator;
30367         
30368     },
30369     
30370     /**
30371      * Mark this field as valid
30372      */
30373     markValid : function()
30374     {
30375         if(this.indicator){
30376             this.indicator.removeClass('visible');
30377             this.indicator.addClass('invisible');
30378         }
30379         
30380         this.el.removeClass(this.invalidClass);
30381         
30382         this.el.addClass(this.validClass);
30383         
30384         this.fireEvent('valid', this);
30385     },
30386     
30387     /**
30388      * Mark this field as invalid
30389      * @param {String} msg The validation message
30390      */
30391     markInvalid : function(msg)
30392     {
30393         if(this.indicator){
30394             this.indicator.removeClass('invisible');
30395             this.indicator.addClass('visible');
30396         }
30397         
30398         this.el.removeClass(this.validClass);
30399         
30400         this.el.addClass(this.invalidClass);
30401         
30402         this.fireEvent('invalid', this, msg);
30403     }
30404     
30405    
30406 });
30407
30408 Roo.apply(Roo.bootstrap.FieldLabel, {
30409     
30410     groups: {},
30411     
30412      /**
30413     * register a FieldLabel Group
30414     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30415     */
30416     register : function(label)
30417     {
30418         if(this.groups.hasOwnProperty(label.target)){
30419             return;
30420         }
30421      
30422         this.groups[label.target] = label;
30423         
30424     },
30425     /**
30426     * fetch a FieldLabel Group based on the target
30427     * @param {string} target
30428     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30429     */
30430     get: function(target) {
30431         if (typeof(this.groups[target]) == 'undefined') {
30432             return false;
30433         }
30434         
30435         return this.groups[target] ;
30436     }
30437 });
30438
30439  
30440
30441  /*
30442  * - LGPL
30443  *
30444  * page DateSplitField.
30445  * 
30446  */
30447
30448
30449 /**
30450  * @class Roo.bootstrap.DateSplitField
30451  * @extends Roo.bootstrap.Component
30452  * Bootstrap DateSplitField class
30453  * @cfg {string} fieldLabel - the label associated
30454  * @cfg {Number} labelWidth set the width of label (0-12)
30455  * @cfg {String} labelAlign (top|left)
30456  * @cfg {Boolean} dayAllowBlank (true|false) default false
30457  * @cfg {Boolean} monthAllowBlank (true|false) default false
30458  * @cfg {Boolean} yearAllowBlank (true|false) default false
30459  * @cfg {string} dayPlaceholder 
30460  * @cfg {string} monthPlaceholder
30461  * @cfg {string} yearPlaceholder
30462  * @cfg {string} dayFormat default 'd'
30463  * @cfg {string} monthFormat default 'm'
30464  * @cfg {string} yearFormat default 'Y'
30465  * @cfg {Number} labellg set the width of label (1-12)
30466  * @cfg {Number} labelmd set the width of label (1-12)
30467  * @cfg {Number} labelsm set the width of label (1-12)
30468  * @cfg {Number} labelxs set the width of label (1-12)
30469
30470  *     
30471  * @constructor
30472  * Create a new DateSplitField
30473  * @param {Object} config The config object
30474  */
30475
30476 Roo.bootstrap.DateSplitField = function(config){
30477     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30478     
30479     this.addEvents({
30480         // raw events
30481          /**
30482          * @event years
30483          * getting the data of years
30484          * @param {Roo.bootstrap.DateSplitField} this
30485          * @param {Object} years
30486          */
30487         "years" : true,
30488         /**
30489          * @event days
30490          * getting the data of days
30491          * @param {Roo.bootstrap.DateSplitField} this
30492          * @param {Object} days
30493          */
30494         "days" : true,
30495         /**
30496          * @event invalid
30497          * Fires after the field has been marked as invalid.
30498          * @param {Roo.form.Field} this
30499          * @param {String} msg The validation message
30500          */
30501         invalid : true,
30502        /**
30503          * @event valid
30504          * Fires after the field has been validated with no errors.
30505          * @param {Roo.form.Field} this
30506          */
30507         valid : true
30508     });
30509 };
30510
30511 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30512     
30513     fieldLabel : '',
30514     labelAlign : 'top',
30515     labelWidth : 3,
30516     dayAllowBlank : false,
30517     monthAllowBlank : false,
30518     yearAllowBlank : false,
30519     dayPlaceholder : '',
30520     monthPlaceholder : '',
30521     yearPlaceholder : '',
30522     dayFormat : 'd',
30523     monthFormat : 'm',
30524     yearFormat : 'Y',
30525     isFormField : true,
30526     labellg : 0,
30527     labelmd : 0,
30528     labelsm : 0,
30529     labelxs : 0,
30530     
30531     getAutoCreate : function()
30532     {
30533         var cfg = {
30534             tag : 'div',
30535             cls : 'row roo-date-split-field-group',
30536             cn : [
30537                 {
30538                     tag : 'input',
30539                     type : 'hidden',
30540                     cls : 'form-hidden-field roo-date-split-field-group-value',
30541                     name : this.name
30542                 }
30543             ]
30544         };
30545         
30546         var labelCls = 'col-md-12';
30547         var contentCls = 'col-md-4';
30548         
30549         if(this.fieldLabel){
30550             
30551             var label = {
30552                 tag : 'div',
30553                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30554                 cn : [
30555                     {
30556                         tag : 'label',
30557                         html : this.fieldLabel
30558                     }
30559                 ]
30560             };
30561             
30562             if(this.labelAlign == 'left'){
30563             
30564                 if(this.labelWidth > 12){
30565                     label.style = "width: " + this.labelWidth + 'px';
30566                 }
30567
30568                 if(this.labelWidth < 13 && this.labelmd == 0){
30569                     this.labelmd = this.labelWidth;
30570                 }
30571
30572                 if(this.labellg > 0){
30573                     labelCls = ' col-lg-' + this.labellg;
30574                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30575                 }
30576
30577                 if(this.labelmd > 0){
30578                     labelCls = ' col-md-' + this.labelmd;
30579                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30580                 }
30581
30582                 if(this.labelsm > 0){
30583                     labelCls = ' col-sm-' + this.labelsm;
30584                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30585                 }
30586
30587                 if(this.labelxs > 0){
30588                     labelCls = ' col-xs-' + this.labelxs;
30589                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30590                 }
30591             }
30592             
30593             label.cls += ' ' + labelCls;
30594             
30595             cfg.cn.push(label);
30596         }
30597         
30598         Roo.each(['day', 'month', 'year'], function(t){
30599             cfg.cn.push({
30600                 tag : 'div',
30601                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30602             });
30603         }, this);
30604         
30605         return cfg;
30606     },
30607     
30608     inputEl: function ()
30609     {
30610         return this.el.select('.roo-date-split-field-group-value', true).first();
30611     },
30612     
30613     onRender : function(ct, position) 
30614     {
30615         var _this = this;
30616         
30617         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30618         
30619         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30620         
30621         this.dayField = new Roo.bootstrap.ComboBox({
30622             allowBlank : this.dayAllowBlank,
30623             alwaysQuery : true,
30624             displayField : 'value',
30625             editable : false,
30626             fieldLabel : '',
30627             forceSelection : true,
30628             mode : 'local',
30629             placeholder : this.dayPlaceholder,
30630             selectOnFocus : true,
30631             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30632             triggerAction : 'all',
30633             typeAhead : true,
30634             valueField : 'value',
30635             store : new Roo.data.SimpleStore({
30636                 data : (function() {    
30637                     var days = [];
30638                     _this.fireEvent('days', _this, days);
30639                     return days;
30640                 })(),
30641                 fields : [ 'value' ]
30642             }),
30643             listeners : {
30644                 select : function (_self, record, index)
30645                 {
30646                     _this.setValue(_this.getValue());
30647                 }
30648             }
30649         });
30650
30651         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30652         
30653         this.monthField = new Roo.bootstrap.MonthField({
30654             after : '<i class=\"fa fa-calendar\"></i>',
30655             allowBlank : this.monthAllowBlank,
30656             placeholder : this.monthPlaceholder,
30657             readOnly : true,
30658             listeners : {
30659                 render : function (_self)
30660                 {
30661                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30662                         e.preventDefault();
30663                         _self.focus();
30664                     });
30665                 },
30666                 select : function (_self, oldvalue, newvalue)
30667                 {
30668                     _this.setValue(_this.getValue());
30669                 }
30670             }
30671         });
30672         
30673         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30674         
30675         this.yearField = new Roo.bootstrap.ComboBox({
30676             allowBlank : this.yearAllowBlank,
30677             alwaysQuery : true,
30678             displayField : 'value',
30679             editable : false,
30680             fieldLabel : '',
30681             forceSelection : true,
30682             mode : 'local',
30683             placeholder : this.yearPlaceholder,
30684             selectOnFocus : true,
30685             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30686             triggerAction : 'all',
30687             typeAhead : true,
30688             valueField : 'value',
30689             store : new Roo.data.SimpleStore({
30690                 data : (function() {
30691                     var years = [];
30692                     _this.fireEvent('years', _this, years);
30693                     return years;
30694                 })(),
30695                 fields : [ 'value' ]
30696             }),
30697             listeners : {
30698                 select : function (_self, record, index)
30699                 {
30700                     _this.setValue(_this.getValue());
30701                 }
30702             }
30703         });
30704
30705         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30706     },
30707     
30708     setValue : function(v, format)
30709     {
30710         this.inputEl.dom.value = v;
30711         
30712         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30713         
30714         var d = Date.parseDate(v, f);
30715         
30716         if(!d){
30717             this.validate();
30718             return;
30719         }
30720         
30721         this.setDay(d.format(this.dayFormat));
30722         this.setMonth(d.format(this.monthFormat));
30723         this.setYear(d.format(this.yearFormat));
30724         
30725         this.validate();
30726         
30727         return;
30728     },
30729     
30730     setDay : function(v)
30731     {
30732         this.dayField.setValue(v);
30733         this.inputEl.dom.value = this.getValue();
30734         this.validate();
30735         return;
30736     },
30737     
30738     setMonth : function(v)
30739     {
30740         this.monthField.setValue(v, true);
30741         this.inputEl.dom.value = this.getValue();
30742         this.validate();
30743         return;
30744     },
30745     
30746     setYear : function(v)
30747     {
30748         this.yearField.setValue(v);
30749         this.inputEl.dom.value = this.getValue();
30750         this.validate();
30751         return;
30752     },
30753     
30754     getDay : function()
30755     {
30756         return this.dayField.getValue();
30757     },
30758     
30759     getMonth : function()
30760     {
30761         return this.monthField.getValue();
30762     },
30763     
30764     getYear : function()
30765     {
30766         return this.yearField.getValue();
30767     },
30768     
30769     getValue : function()
30770     {
30771         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30772         
30773         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30774         
30775         return date;
30776     },
30777     
30778     reset : function()
30779     {
30780         this.setDay('');
30781         this.setMonth('');
30782         this.setYear('');
30783         this.inputEl.dom.value = '';
30784         this.validate();
30785         return;
30786     },
30787     
30788     validate : function()
30789     {
30790         var d = this.dayField.validate();
30791         var m = this.monthField.validate();
30792         var y = this.yearField.validate();
30793         
30794         var valid = true;
30795         
30796         if(
30797                 (!this.dayAllowBlank && !d) ||
30798                 (!this.monthAllowBlank && !m) ||
30799                 (!this.yearAllowBlank && !y)
30800         ){
30801             valid = false;
30802         }
30803         
30804         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30805             return valid;
30806         }
30807         
30808         if(valid){
30809             this.markValid();
30810             return valid;
30811         }
30812         
30813         this.markInvalid();
30814         
30815         return valid;
30816     },
30817     
30818     markValid : function()
30819     {
30820         
30821         var label = this.el.select('label', true).first();
30822         var icon = this.el.select('i.fa-star', true).first();
30823
30824         if(label && icon){
30825             icon.remove();
30826         }
30827         
30828         this.fireEvent('valid', this);
30829     },
30830     
30831      /**
30832      * Mark this field as invalid
30833      * @param {String} msg The validation message
30834      */
30835     markInvalid : function(msg)
30836     {
30837         
30838         var label = this.el.select('label', true).first();
30839         var icon = this.el.select('i.fa-star', true).first();
30840
30841         if(label && !icon){
30842             this.el.select('.roo-date-split-field-label', true).createChild({
30843                 tag : 'i',
30844                 cls : 'text-danger fa fa-lg fa-star',
30845                 tooltip : 'This field is required',
30846                 style : 'margin-right:5px;'
30847             }, label, true);
30848         }
30849         
30850         this.fireEvent('invalid', this, msg);
30851     },
30852     
30853     clearInvalid : function()
30854     {
30855         var label = this.el.select('label', true).first();
30856         var icon = this.el.select('i.fa-star', true).first();
30857
30858         if(label && icon){
30859             icon.remove();
30860         }
30861         
30862         this.fireEvent('valid', this);
30863     },
30864     
30865     getName: function()
30866     {
30867         return this.name;
30868     }
30869     
30870 });
30871
30872  /**
30873  *
30874  * This is based on 
30875  * http://masonry.desandro.com
30876  *
30877  * The idea is to render all the bricks based on vertical width...
30878  *
30879  * The original code extends 'outlayer' - we might need to use that....
30880  * 
30881  */
30882
30883
30884 /**
30885  * @class Roo.bootstrap.LayoutMasonry
30886  * @extends Roo.bootstrap.Component
30887  * Bootstrap Layout Masonry class
30888  * 
30889  * @constructor
30890  * Create a new Element
30891  * @param {Object} config The config object
30892  */
30893
30894 Roo.bootstrap.LayoutMasonry = function(config){
30895     
30896     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30897     
30898     this.bricks = [];
30899     
30900     Roo.bootstrap.LayoutMasonry.register(this);
30901     
30902     this.addEvents({
30903         // raw events
30904         /**
30905          * @event layout
30906          * Fire after layout the items
30907          * @param {Roo.bootstrap.LayoutMasonry} this
30908          * @param {Roo.EventObject} e
30909          */
30910         "layout" : true
30911     });
30912     
30913 };
30914
30915 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30916     
30917     /**
30918      * @cfg {Boolean} isLayoutInstant = no animation?
30919      */   
30920     isLayoutInstant : false, // needed?
30921    
30922     /**
30923      * @cfg {Number} boxWidth  width of the columns
30924      */   
30925     boxWidth : 450,
30926     
30927       /**
30928      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30929      */   
30930     boxHeight : 0,
30931     
30932     /**
30933      * @cfg {Number} padWidth padding below box..
30934      */   
30935     padWidth : 10, 
30936     
30937     /**
30938      * @cfg {Number} gutter gutter width..
30939      */   
30940     gutter : 10,
30941     
30942      /**
30943      * @cfg {Number} maxCols maximum number of columns
30944      */   
30945     
30946     maxCols: 0,
30947     
30948     /**
30949      * @cfg {Boolean} isAutoInitial defalut true
30950      */   
30951     isAutoInitial : true, 
30952     
30953     containerWidth: 0,
30954     
30955     /**
30956      * @cfg {Boolean} isHorizontal defalut false
30957      */   
30958     isHorizontal : false, 
30959
30960     currentSize : null,
30961     
30962     tag: 'div',
30963     
30964     cls: '',
30965     
30966     bricks: null, //CompositeElement
30967     
30968     cols : 1,
30969     
30970     _isLayoutInited : false,
30971     
30972 //    isAlternative : false, // only use for vertical layout...
30973     
30974     /**
30975      * @cfg {Number} alternativePadWidth padding below box..
30976      */   
30977     alternativePadWidth : 50,
30978     
30979     selectedBrick : [],
30980     
30981     getAutoCreate : function(){
30982         
30983         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30984         
30985         var cfg = {
30986             tag: this.tag,
30987             cls: 'blog-masonary-wrapper ' + this.cls,
30988             cn : {
30989                 cls : 'mas-boxes masonary'
30990             }
30991         };
30992         
30993         return cfg;
30994     },
30995     
30996     getChildContainer: function( )
30997     {
30998         if (this.boxesEl) {
30999             return this.boxesEl;
31000         }
31001         
31002         this.boxesEl = this.el.select('.mas-boxes').first();
31003         
31004         return this.boxesEl;
31005     },
31006     
31007     
31008     initEvents : function()
31009     {
31010         var _this = this;
31011         
31012         if(this.isAutoInitial){
31013             Roo.log('hook children rendered');
31014             this.on('childrenrendered', function() {
31015                 Roo.log('children rendered');
31016                 _this.initial();
31017             } ,this);
31018         }
31019     },
31020     
31021     initial : function()
31022     {
31023         this.selectedBrick = [];
31024         
31025         this.currentSize = this.el.getBox(true);
31026         
31027         Roo.EventManager.onWindowResize(this.resize, this); 
31028
31029         if(!this.isAutoInitial){
31030             this.layout();
31031             return;
31032         }
31033         
31034         this.layout();
31035         
31036         return;
31037         //this.layout.defer(500,this);
31038         
31039     },
31040     
31041     resize : function()
31042     {
31043         var cs = this.el.getBox(true);
31044         
31045         if (
31046                 this.currentSize.width == cs.width && 
31047                 this.currentSize.x == cs.x && 
31048                 this.currentSize.height == cs.height && 
31049                 this.currentSize.y == cs.y 
31050         ) {
31051             Roo.log("no change in with or X or Y");
31052             return;
31053         }
31054         
31055         this.currentSize = cs;
31056         
31057         this.layout();
31058         
31059     },
31060     
31061     layout : function()
31062     {   
31063         this._resetLayout();
31064         
31065         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31066         
31067         this.layoutItems( isInstant );
31068       
31069         this._isLayoutInited = true;
31070         
31071         this.fireEvent('layout', this);
31072         
31073     },
31074     
31075     _resetLayout : function()
31076     {
31077         if(this.isHorizontal){
31078             this.horizontalMeasureColumns();
31079             return;
31080         }
31081         
31082         this.verticalMeasureColumns();
31083         
31084     },
31085     
31086     verticalMeasureColumns : function()
31087     {
31088         this.getContainerWidth();
31089         
31090 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31091 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31092 //            return;
31093 //        }
31094         
31095         var boxWidth = this.boxWidth + this.padWidth;
31096         
31097         if(this.containerWidth < this.boxWidth){
31098             boxWidth = this.containerWidth
31099         }
31100         
31101         var containerWidth = this.containerWidth;
31102         
31103         var cols = Math.floor(containerWidth / boxWidth);
31104         
31105         this.cols = Math.max( cols, 1 );
31106         
31107         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31108         
31109         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31110         
31111         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31112         
31113         this.colWidth = boxWidth + avail - this.padWidth;
31114         
31115         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31116         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31117     },
31118     
31119     horizontalMeasureColumns : function()
31120     {
31121         this.getContainerWidth();
31122         
31123         var boxWidth = this.boxWidth;
31124         
31125         if(this.containerWidth < boxWidth){
31126             boxWidth = this.containerWidth;
31127         }
31128         
31129         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31130         
31131         this.el.setHeight(boxWidth);
31132         
31133     },
31134     
31135     getContainerWidth : function()
31136     {
31137         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31138     },
31139     
31140     layoutItems : function( isInstant )
31141     {
31142         Roo.log(this.bricks);
31143         
31144         var items = Roo.apply([], this.bricks);
31145         
31146         if(this.isHorizontal){
31147             this._horizontalLayoutItems( items , isInstant );
31148             return;
31149         }
31150         
31151 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31152 //            this._verticalAlternativeLayoutItems( items , isInstant );
31153 //            return;
31154 //        }
31155         
31156         this._verticalLayoutItems( items , isInstant );
31157         
31158     },
31159     
31160     _verticalLayoutItems : function ( items , isInstant)
31161     {
31162         if ( !items || !items.length ) {
31163             return;
31164         }
31165         
31166         var standard = [
31167             ['xs', 'xs', 'xs', 'tall'],
31168             ['xs', 'xs', 'tall'],
31169             ['xs', 'xs', 'sm'],
31170             ['xs', 'xs', 'xs'],
31171             ['xs', 'tall'],
31172             ['xs', 'sm'],
31173             ['xs', 'xs'],
31174             ['xs'],
31175             
31176             ['sm', 'xs', 'xs'],
31177             ['sm', 'xs'],
31178             ['sm'],
31179             
31180             ['tall', 'xs', 'xs', 'xs'],
31181             ['tall', 'xs', 'xs'],
31182             ['tall', 'xs'],
31183             ['tall']
31184             
31185         ];
31186         
31187         var queue = [];
31188         
31189         var boxes = [];
31190         
31191         var box = [];
31192         
31193         Roo.each(items, function(item, k){
31194             
31195             switch (item.size) {
31196                 // these layouts take up a full box,
31197                 case 'md' :
31198                 case 'md-left' :
31199                 case 'md-right' :
31200                 case 'wide' :
31201                     
31202                     if(box.length){
31203                         boxes.push(box);
31204                         box = [];
31205                     }
31206                     
31207                     boxes.push([item]);
31208                     
31209                     break;
31210                     
31211                 case 'xs' :
31212                 case 'sm' :
31213                 case 'tall' :
31214                     
31215                     box.push(item);
31216                     
31217                     break;
31218                 default :
31219                     break;
31220                     
31221             }
31222             
31223         }, this);
31224         
31225         if(box.length){
31226             boxes.push(box);
31227             box = [];
31228         }
31229         
31230         var filterPattern = function(box, length)
31231         {
31232             if(!box.length){
31233                 return;
31234             }
31235             
31236             var match = false;
31237             
31238             var pattern = box.slice(0, length);
31239             
31240             var format = [];
31241             
31242             Roo.each(pattern, function(i){
31243                 format.push(i.size);
31244             }, this);
31245             
31246             Roo.each(standard, function(s){
31247                 
31248                 if(String(s) != String(format)){
31249                     return;
31250                 }
31251                 
31252                 match = true;
31253                 return false;
31254                 
31255             }, this);
31256             
31257             if(!match && length == 1){
31258                 return;
31259             }
31260             
31261             if(!match){
31262                 filterPattern(box, length - 1);
31263                 return;
31264             }
31265                 
31266             queue.push(pattern);
31267
31268             box = box.slice(length, box.length);
31269
31270             filterPattern(box, 4);
31271
31272             return;
31273             
31274         }
31275         
31276         Roo.each(boxes, function(box, k){
31277             
31278             if(!box.length){
31279                 return;
31280             }
31281             
31282             if(box.length == 1){
31283                 queue.push(box);
31284                 return;
31285             }
31286             
31287             filterPattern(box, 4);
31288             
31289         }, this);
31290         
31291         this._processVerticalLayoutQueue( queue, isInstant );
31292         
31293     },
31294     
31295 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31296 //    {
31297 //        if ( !items || !items.length ) {
31298 //            return;
31299 //        }
31300 //
31301 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31302 //        
31303 //    },
31304     
31305     _horizontalLayoutItems : function ( items , isInstant)
31306     {
31307         if ( !items || !items.length || items.length < 3) {
31308             return;
31309         }
31310         
31311         items.reverse();
31312         
31313         var eItems = items.slice(0, 3);
31314         
31315         items = items.slice(3, items.length);
31316         
31317         var standard = [
31318             ['xs', 'xs', 'xs', 'wide'],
31319             ['xs', 'xs', 'wide'],
31320             ['xs', 'xs', 'sm'],
31321             ['xs', 'xs', 'xs'],
31322             ['xs', 'wide'],
31323             ['xs', 'sm'],
31324             ['xs', 'xs'],
31325             ['xs'],
31326             
31327             ['sm', 'xs', 'xs'],
31328             ['sm', 'xs'],
31329             ['sm'],
31330             
31331             ['wide', 'xs', 'xs', 'xs'],
31332             ['wide', 'xs', 'xs'],
31333             ['wide', 'xs'],
31334             ['wide'],
31335             
31336             ['wide-thin']
31337         ];
31338         
31339         var queue = [];
31340         
31341         var boxes = [];
31342         
31343         var box = [];
31344         
31345         Roo.each(items, function(item, k){
31346             
31347             switch (item.size) {
31348                 case 'md' :
31349                 case 'md-left' :
31350                 case 'md-right' :
31351                 case 'tall' :
31352                     
31353                     if(box.length){
31354                         boxes.push(box);
31355                         box = [];
31356                     }
31357                     
31358                     boxes.push([item]);
31359                     
31360                     break;
31361                     
31362                 case 'xs' :
31363                 case 'sm' :
31364                 case 'wide' :
31365                 case 'wide-thin' :
31366                     
31367                     box.push(item);
31368                     
31369                     break;
31370                 default :
31371                     break;
31372                     
31373             }
31374             
31375         }, this);
31376         
31377         if(box.length){
31378             boxes.push(box);
31379             box = [];
31380         }
31381         
31382         var filterPattern = function(box, length)
31383         {
31384             if(!box.length){
31385                 return;
31386             }
31387             
31388             var match = false;
31389             
31390             var pattern = box.slice(0, length);
31391             
31392             var format = [];
31393             
31394             Roo.each(pattern, function(i){
31395                 format.push(i.size);
31396             }, this);
31397             
31398             Roo.each(standard, function(s){
31399                 
31400                 if(String(s) != String(format)){
31401                     return;
31402                 }
31403                 
31404                 match = true;
31405                 return false;
31406                 
31407             }, this);
31408             
31409             if(!match && length == 1){
31410                 return;
31411             }
31412             
31413             if(!match){
31414                 filterPattern(box, length - 1);
31415                 return;
31416             }
31417                 
31418             queue.push(pattern);
31419
31420             box = box.slice(length, box.length);
31421
31422             filterPattern(box, 4);
31423
31424             return;
31425             
31426         }
31427         
31428         Roo.each(boxes, function(box, k){
31429             
31430             if(!box.length){
31431                 return;
31432             }
31433             
31434             if(box.length == 1){
31435                 queue.push(box);
31436                 return;
31437             }
31438             
31439             filterPattern(box, 4);
31440             
31441         }, this);
31442         
31443         
31444         var prune = [];
31445         
31446         var pos = this.el.getBox(true);
31447         
31448         var minX = pos.x;
31449         
31450         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31451         
31452         var hit_end = false;
31453         
31454         Roo.each(queue, function(box){
31455             
31456             if(hit_end){
31457                 
31458                 Roo.each(box, function(b){
31459                 
31460                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31461                     b.el.hide();
31462
31463                 }, this);
31464
31465                 return;
31466             }
31467             
31468             var mx = 0;
31469             
31470             Roo.each(box, function(b){
31471                 
31472                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31473                 b.el.show();
31474
31475                 mx = Math.max(mx, b.x);
31476                 
31477             }, this);
31478             
31479             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31480             
31481             if(maxX < minX){
31482                 
31483                 Roo.each(box, function(b){
31484                 
31485                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31486                     b.el.hide();
31487                     
31488                 }, this);
31489                 
31490                 hit_end = true;
31491                 
31492                 return;
31493             }
31494             
31495             prune.push(box);
31496             
31497         }, this);
31498         
31499         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31500     },
31501     
31502     /** Sets position of item in DOM
31503     * @param {Element} item
31504     * @param {Number} x - horizontal position
31505     * @param {Number} y - vertical position
31506     * @param {Boolean} isInstant - disables transitions
31507     */
31508     _processVerticalLayoutQueue : function( queue, isInstant )
31509     {
31510         var pos = this.el.getBox(true);
31511         var x = pos.x;
31512         var y = pos.y;
31513         var maxY = [];
31514         
31515         for (var i = 0; i < this.cols; i++){
31516             maxY[i] = pos.y;
31517         }
31518         
31519         Roo.each(queue, function(box, k){
31520             
31521             var col = k % this.cols;
31522             
31523             Roo.each(box, function(b,kk){
31524                 
31525                 b.el.position('absolute');
31526                 
31527                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31528                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31529                 
31530                 if(b.size == 'md-left' || b.size == 'md-right'){
31531                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31532                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31533                 }
31534                 
31535                 b.el.setWidth(width);
31536                 b.el.setHeight(height);
31537                 // iframe?
31538                 b.el.select('iframe',true).setSize(width,height);
31539                 
31540             }, this);
31541             
31542             for (var i = 0; i < this.cols; i++){
31543                 
31544                 if(maxY[i] < maxY[col]){
31545                     col = i;
31546                     continue;
31547                 }
31548                 
31549                 col = Math.min(col, i);
31550                 
31551             }
31552             
31553             x = pos.x + col * (this.colWidth + this.padWidth);
31554             
31555             y = maxY[col];
31556             
31557             var positions = [];
31558             
31559             switch (box.length){
31560                 case 1 :
31561                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31562                     break;
31563                 case 2 :
31564                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31565                     break;
31566                 case 3 :
31567                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31568                     break;
31569                 case 4 :
31570                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31571                     break;
31572                 default :
31573                     break;
31574             }
31575             
31576             Roo.each(box, function(b,kk){
31577                 
31578                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31579                 
31580                 var sz = b.el.getSize();
31581                 
31582                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31583                 
31584             }, this);
31585             
31586         }, this);
31587         
31588         var mY = 0;
31589         
31590         for (var i = 0; i < this.cols; i++){
31591             mY = Math.max(mY, maxY[i]);
31592         }
31593         
31594         this.el.setHeight(mY - pos.y);
31595         
31596     },
31597     
31598 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31599 //    {
31600 //        var pos = this.el.getBox(true);
31601 //        var x = pos.x;
31602 //        var y = pos.y;
31603 //        var maxX = pos.right;
31604 //        
31605 //        var maxHeight = 0;
31606 //        
31607 //        Roo.each(items, function(item, k){
31608 //            
31609 //            var c = k % 2;
31610 //            
31611 //            item.el.position('absolute');
31612 //                
31613 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31614 //
31615 //            item.el.setWidth(width);
31616 //
31617 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31618 //
31619 //            item.el.setHeight(height);
31620 //            
31621 //            if(c == 0){
31622 //                item.el.setXY([x, y], isInstant ? false : true);
31623 //            } else {
31624 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31625 //            }
31626 //            
31627 //            y = y + height + this.alternativePadWidth;
31628 //            
31629 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31630 //            
31631 //        }, this);
31632 //        
31633 //        this.el.setHeight(maxHeight);
31634 //        
31635 //    },
31636     
31637     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31638     {
31639         var pos = this.el.getBox(true);
31640         
31641         var minX = pos.x;
31642         var minY = pos.y;
31643         
31644         var maxX = pos.right;
31645         
31646         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31647         
31648         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31649         
31650         Roo.each(queue, function(box, k){
31651             
31652             Roo.each(box, function(b, kk){
31653                 
31654                 b.el.position('absolute');
31655                 
31656                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31657                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31658                 
31659                 if(b.size == 'md-left' || b.size == 'md-right'){
31660                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31661                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31662                 }
31663                 
31664                 b.el.setWidth(width);
31665                 b.el.setHeight(height);
31666                 
31667             }, this);
31668             
31669             if(!box.length){
31670                 return;
31671             }
31672             
31673             var positions = [];
31674             
31675             switch (box.length){
31676                 case 1 :
31677                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31678                     break;
31679                 case 2 :
31680                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31681                     break;
31682                 case 3 :
31683                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31684                     break;
31685                 case 4 :
31686                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31687                     break;
31688                 default :
31689                     break;
31690             }
31691             
31692             Roo.each(box, function(b,kk){
31693                 
31694                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31695                 
31696                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31697                 
31698             }, this);
31699             
31700         }, this);
31701         
31702     },
31703     
31704     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31705     {
31706         Roo.each(eItems, function(b,k){
31707             
31708             b.size = (k == 0) ? 'sm' : 'xs';
31709             b.x = (k == 0) ? 2 : 1;
31710             b.y = (k == 0) ? 2 : 1;
31711             
31712             b.el.position('absolute');
31713             
31714             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31715                 
31716             b.el.setWidth(width);
31717             
31718             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31719             
31720             b.el.setHeight(height);
31721             
31722         }, this);
31723
31724         var positions = [];
31725         
31726         positions.push({
31727             x : maxX - this.unitWidth * 2 - this.gutter,
31728             y : minY
31729         });
31730         
31731         positions.push({
31732             x : maxX - this.unitWidth,
31733             y : minY + (this.unitWidth + this.gutter) * 2
31734         });
31735         
31736         positions.push({
31737             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31738             y : minY
31739         });
31740         
31741         Roo.each(eItems, function(b,k){
31742             
31743             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31744
31745         }, this);
31746         
31747     },
31748     
31749     getVerticalOneBoxColPositions : function(x, y, box)
31750     {
31751         var pos = [];
31752         
31753         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31754         
31755         if(box[0].size == 'md-left'){
31756             rand = 0;
31757         }
31758         
31759         if(box[0].size == 'md-right'){
31760             rand = 1;
31761         }
31762         
31763         pos.push({
31764             x : x + (this.unitWidth + this.gutter) * rand,
31765             y : y
31766         });
31767         
31768         return pos;
31769     },
31770     
31771     getVerticalTwoBoxColPositions : function(x, y, box)
31772     {
31773         var pos = [];
31774         
31775         if(box[0].size == 'xs'){
31776             
31777             pos.push({
31778                 x : x,
31779                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31780             });
31781
31782             pos.push({
31783                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31784                 y : y
31785             });
31786             
31787             return pos;
31788             
31789         }
31790         
31791         pos.push({
31792             x : x,
31793             y : y
31794         });
31795
31796         pos.push({
31797             x : x + (this.unitWidth + this.gutter) * 2,
31798             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31799         });
31800         
31801         return pos;
31802         
31803     },
31804     
31805     getVerticalThreeBoxColPositions : function(x, y, box)
31806     {
31807         var pos = [];
31808         
31809         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31810             
31811             pos.push({
31812                 x : x,
31813                 y : y
31814             });
31815
31816             pos.push({
31817                 x : x + (this.unitWidth + this.gutter) * 1,
31818                 y : y
31819             });
31820             
31821             pos.push({
31822                 x : x + (this.unitWidth + this.gutter) * 2,
31823                 y : y
31824             });
31825             
31826             return pos;
31827             
31828         }
31829         
31830         if(box[0].size == 'xs' && box[1].size == 'xs'){
31831             
31832             pos.push({
31833                 x : x,
31834                 y : y
31835             });
31836
31837             pos.push({
31838                 x : x,
31839                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31840             });
31841             
31842             pos.push({
31843                 x : x + (this.unitWidth + this.gutter) * 1,
31844                 y : y
31845             });
31846             
31847             return pos;
31848             
31849         }
31850         
31851         pos.push({
31852             x : x,
31853             y : y
31854         });
31855
31856         pos.push({
31857             x : x + (this.unitWidth + this.gutter) * 2,
31858             y : y
31859         });
31860
31861         pos.push({
31862             x : x + (this.unitWidth + this.gutter) * 2,
31863             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31864         });
31865             
31866         return pos;
31867         
31868     },
31869     
31870     getVerticalFourBoxColPositions : function(x, y, box)
31871     {
31872         var pos = [];
31873         
31874         if(box[0].size == 'xs'){
31875             
31876             pos.push({
31877                 x : x,
31878                 y : y
31879             });
31880
31881             pos.push({
31882                 x : x,
31883                 y : y + (this.unitHeight + this.gutter) * 1
31884             });
31885             
31886             pos.push({
31887                 x : x,
31888                 y : y + (this.unitHeight + this.gutter) * 2
31889             });
31890             
31891             pos.push({
31892                 x : x + (this.unitWidth + this.gutter) * 1,
31893                 y : y
31894             });
31895             
31896             return pos;
31897             
31898         }
31899         
31900         pos.push({
31901             x : x,
31902             y : y
31903         });
31904
31905         pos.push({
31906             x : x + (this.unitWidth + this.gutter) * 2,
31907             y : y
31908         });
31909
31910         pos.push({
31911             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31912             y : y + (this.unitHeight + this.gutter) * 1
31913         });
31914
31915         pos.push({
31916             x : x + (this.unitWidth + this.gutter) * 2,
31917             y : y + (this.unitWidth + this.gutter) * 2
31918         });
31919
31920         return pos;
31921         
31922     },
31923     
31924     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31925     {
31926         var pos = [];
31927         
31928         if(box[0].size == 'md-left'){
31929             pos.push({
31930                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31931                 y : minY
31932             });
31933             
31934             return pos;
31935         }
31936         
31937         if(box[0].size == 'md-right'){
31938             pos.push({
31939                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31940                 y : minY + (this.unitWidth + this.gutter) * 1
31941             });
31942             
31943             return pos;
31944         }
31945         
31946         var rand = Math.floor(Math.random() * (4 - box[0].y));
31947         
31948         pos.push({
31949             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31950             y : minY + (this.unitWidth + this.gutter) * rand
31951         });
31952         
31953         return pos;
31954         
31955     },
31956     
31957     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31958     {
31959         var pos = [];
31960         
31961         if(box[0].size == 'xs'){
31962             
31963             pos.push({
31964                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31965                 y : minY
31966             });
31967
31968             pos.push({
31969                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31970                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31971             });
31972             
31973             return pos;
31974             
31975         }
31976         
31977         pos.push({
31978             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31979             y : minY
31980         });
31981
31982         pos.push({
31983             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31984             y : minY + (this.unitWidth + this.gutter) * 2
31985         });
31986         
31987         return pos;
31988         
31989     },
31990     
31991     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31992     {
31993         var pos = [];
31994         
31995         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31996             
31997             pos.push({
31998                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31999                 y : minY
32000             });
32001
32002             pos.push({
32003                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32004                 y : minY + (this.unitWidth + this.gutter) * 1
32005             });
32006             
32007             pos.push({
32008                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32009                 y : minY + (this.unitWidth + this.gutter) * 2
32010             });
32011             
32012             return pos;
32013             
32014         }
32015         
32016         if(box[0].size == 'xs' && box[1].size == 'xs'){
32017             
32018             pos.push({
32019                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32020                 y : minY
32021             });
32022
32023             pos.push({
32024                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32025                 y : minY
32026             });
32027             
32028             pos.push({
32029                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32030                 y : minY + (this.unitWidth + this.gutter) * 1
32031             });
32032             
32033             return pos;
32034             
32035         }
32036         
32037         pos.push({
32038             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32039             y : minY
32040         });
32041
32042         pos.push({
32043             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32044             y : minY + (this.unitWidth + this.gutter) * 2
32045         });
32046
32047         pos.push({
32048             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32049             y : minY + (this.unitWidth + this.gutter) * 2
32050         });
32051             
32052         return pos;
32053         
32054     },
32055     
32056     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32057     {
32058         var pos = [];
32059         
32060         if(box[0].size == 'xs'){
32061             
32062             pos.push({
32063                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32064                 y : minY
32065             });
32066
32067             pos.push({
32068                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32069                 y : minY
32070             });
32071             
32072             pos.push({
32073                 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),
32074                 y : minY
32075             });
32076             
32077             pos.push({
32078                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32079                 y : minY + (this.unitWidth + this.gutter) * 1
32080             });
32081             
32082             return pos;
32083             
32084         }
32085         
32086         pos.push({
32087             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32088             y : minY
32089         });
32090         
32091         pos.push({
32092             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32093             y : minY + (this.unitWidth + this.gutter) * 2
32094         });
32095         
32096         pos.push({
32097             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32098             y : minY + (this.unitWidth + this.gutter) * 2
32099         });
32100         
32101         pos.push({
32102             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),
32103             y : minY + (this.unitWidth + this.gutter) * 2
32104         });
32105
32106         return pos;
32107         
32108     },
32109     
32110     /**
32111     * remove a Masonry Brick
32112     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32113     */
32114     removeBrick : function(brick_id)
32115     {
32116         if (!brick_id) {
32117             return;
32118         }
32119         
32120         for (var i = 0; i<this.bricks.length; i++) {
32121             if (this.bricks[i].id == brick_id) {
32122                 this.bricks.splice(i,1);
32123                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32124                 this.initial();
32125             }
32126         }
32127     },
32128     
32129     /**
32130     * adds a Masonry Brick
32131     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32132     */
32133     addBrick : function(cfg)
32134     {
32135         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32136         //this.register(cn);
32137         cn.parentId = this.id;
32138         cn.render(this.el);
32139         return cn;
32140     },
32141     
32142     /**
32143     * register a Masonry Brick
32144     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32145     */
32146     
32147     register : function(brick)
32148     {
32149         this.bricks.push(brick);
32150         brick.masonryId = this.id;
32151     },
32152     
32153     /**
32154     * clear all the Masonry Brick
32155     */
32156     clearAll : function()
32157     {
32158         this.bricks = [];
32159         //this.getChildContainer().dom.innerHTML = "";
32160         this.el.dom.innerHTML = '';
32161     },
32162     
32163     getSelected : function()
32164     {
32165         if (!this.selectedBrick) {
32166             return false;
32167         }
32168         
32169         return this.selectedBrick;
32170     }
32171 });
32172
32173 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32174     
32175     groups: {},
32176      /**
32177     * register a Masonry Layout
32178     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32179     */
32180     
32181     register : function(layout)
32182     {
32183         this.groups[layout.id] = layout;
32184     },
32185     /**
32186     * fetch a  Masonry Layout based on the masonry layout ID
32187     * @param {string} the masonry layout to add
32188     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32189     */
32190     
32191     get: function(layout_id) {
32192         if (typeof(this.groups[layout_id]) == 'undefined') {
32193             return false;
32194         }
32195         return this.groups[layout_id] ;
32196     }
32197     
32198     
32199     
32200 });
32201
32202  
32203
32204  /**
32205  *
32206  * This is based on 
32207  * http://masonry.desandro.com
32208  *
32209  * The idea is to render all the bricks based on vertical width...
32210  *
32211  * The original code extends 'outlayer' - we might need to use that....
32212  * 
32213  */
32214
32215
32216 /**
32217  * @class Roo.bootstrap.LayoutMasonryAuto
32218  * @extends Roo.bootstrap.Component
32219  * Bootstrap Layout Masonry class
32220  * 
32221  * @constructor
32222  * Create a new Element
32223  * @param {Object} config The config object
32224  */
32225
32226 Roo.bootstrap.LayoutMasonryAuto = function(config){
32227     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32228 };
32229
32230 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32231     
32232       /**
32233      * @cfg {Boolean} isFitWidth  - resize the width..
32234      */   
32235     isFitWidth : false,  // options..
32236     /**
32237      * @cfg {Boolean} isOriginLeft = left align?
32238      */   
32239     isOriginLeft : true,
32240     /**
32241      * @cfg {Boolean} isOriginTop = top align?
32242      */   
32243     isOriginTop : false,
32244     /**
32245      * @cfg {Boolean} isLayoutInstant = no animation?
32246      */   
32247     isLayoutInstant : false, // needed?
32248     /**
32249      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32250      */   
32251     isResizingContainer : true,
32252     /**
32253      * @cfg {Number} columnWidth  width of the columns 
32254      */   
32255     
32256     columnWidth : 0,
32257     
32258     /**
32259      * @cfg {Number} maxCols maximum number of columns
32260      */   
32261     
32262     maxCols: 0,
32263     /**
32264      * @cfg {Number} padHeight padding below box..
32265      */   
32266     
32267     padHeight : 10, 
32268     
32269     /**
32270      * @cfg {Boolean} isAutoInitial defalut true
32271      */   
32272     
32273     isAutoInitial : true, 
32274     
32275     // private?
32276     gutter : 0,
32277     
32278     containerWidth: 0,
32279     initialColumnWidth : 0,
32280     currentSize : null,
32281     
32282     colYs : null, // array.
32283     maxY : 0,
32284     padWidth: 10,
32285     
32286     
32287     tag: 'div',
32288     cls: '',
32289     bricks: null, //CompositeElement
32290     cols : 0, // array?
32291     // element : null, // wrapped now this.el
32292     _isLayoutInited : null, 
32293     
32294     
32295     getAutoCreate : function(){
32296         
32297         var cfg = {
32298             tag: this.tag,
32299             cls: 'blog-masonary-wrapper ' + this.cls,
32300             cn : {
32301                 cls : 'mas-boxes masonary'
32302             }
32303         };
32304         
32305         return cfg;
32306     },
32307     
32308     getChildContainer: function( )
32309     {
32310         if (this.boxesEl) {
32311             return this.boxesEl;
32312         }
32313         
32314         this.boxesEl = this.el.select('.mas-boxes').first();
32315         
32316         return this.boxesEl;
32317     },
32318     
32319     
32320     initEvents : function()
32321     {
32322         var _this = this;
32323         
32324         if(this.isAutoInitial){
32325             Roo.log('hook children rendered');
32326             this.on('childrenrendered', function() {
32327                 Roo.log('children rendered');
32328                 _this.initial();
32329             } ,this);
32330         }
32331         
32332     },
32333     
32334     initial : function()
32335     {
32336         this.reloadItems();
32337
32338         this.currentSize = this.el.getBox(true);
32339
32340         /// was window resize... - let's see if this works..
32341         Roo.EventManager.onWindowResize(this.resize, this); 
32342
32343         if(!this.isAutoInitial){
32344             this.layout();
32345             return;
32346         }
32347         
32348         this.layout.defer(500,this);
32349     },
32350     
32351     reloadItems: function()
32352     {
32353         this.bricks = this.el.select('.masonry-brick', true);
32354         
32355         this.bricks.each(function(b) {
32356             //Roo.log(b.getSize());
32357             if (!b.attr('originalwidth')) {
32358                 b.attr('originalwidth',  b.getSize().width);
32359             }
32360             
32361         });
32362         
32363         Roo.log(this.bricks.elements.length);
32364     },
32365     
32366     resize : function()
32367     {
32368         Roo.log('resize');
32369         var cs = this.el.getBox(true);
32370         
32371         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32372             Roo.log("no change in with or X");
32373             return;
32374         }
32375         this.currentSize = cs;
32376         this.layout();
32377     },
32378     
32379     layout : function()
32380     {
32381          Roo.log('layout');
32382         this._resetLayout();
32383         //this._manageStamps();
32384       
32385         // don't animate first layout
32386         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32387         this.layoutItems( isInstant );
32388       
32389         // flag for initalized
32390         this._isLayoutInited = true;
32391     },
32392     
32393     layoutItems : function( isInstant )
32394     {
32395         //var items = this._getItemsForLayout( this.items );
32396         // original code supports filtering layout items.. we just ignore it..
32397         
32398         this._layoutItems( this.bricks , isInstant );
32399       
32400         this._postLayout();
32401     },
32402     _layoutItems : function ( items , isInstant)
32403     {
32404        //this.fireEvent( 'layout', this, items );
32405     
32406
32407         if ( !items || !items.elements.length ) {
32408           // no items, emit event with empty array
32409             return;
32410         }
32411
32412         var queue = [];
32413         items.each(function(item) {
32414             Roo.log("layout item");
32415             Roo.log(item);
32416             // get x/y object from method
32417             var position = this._getItemLayoutPosition( item );
32418             // enqueue
32419             position.item = item;
32420             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32421             queue.push( position );
32422         }, this);
32423       
32424         this._processLayoutQueue( queue );
32425     },
32426     /** Sets position of item in DOM
32427     * @param {Element} item
32428     * @param {Number} x - horizontal position
32429     * @param {Number} y - vertical position
32430     * @param {Boolean} isInstant - disables transitions
32431     */
32432     _processLayoutQueue : function( queue )
32433     {
32434         for ( var i=0, len = queue.length; i < len; i++ ) {
32435             var obj = queue[i];
32436             obj.item.position('absolute');
32437             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32438         }
32439     },
32440       
32441     
32442     /**
32443     * Any logic you want to do after each layout,
32444     * i.e. size the container
32445     */
32446     _postLayout : function()
32447     {
32448         this.resizeContainer();
32449     },
32450     
32451     resizeContainer : function()
32452     {
32453         if ( !this.isResizingContainer ) {
32454             return;
32455         }
32456         var size = this._getContainerSize();
32457         if ( size ) {
32458             this.el.setSize(size.width,size.height);
32459             this.boxesEl.setSize(size.width,size.height);
32460         }
32461     },
32462     
32463     
32464     
32465     _resetLayout : function()
32466     {
32467         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32468         this.colWidth = this.el.getWidth();
32469         //this.gutter = this.el.getWidth(); 
32470         
32471         this.measureColumns();
32472
32473         // reset column Y
32474         var i = this.cols;
32475         this.colYs = [];
32476         while (i--) {
32477             this.colYs.push( 0 );
32478         }
32479     
32480         this.maxY = 0;
32481     },
32482
32483     measureColumns : function()
32484     {
32485         this.getContainerWidth();
32486       // if columnWidth is 0, default to outerWidth of first item
32487         if ( !this.columnWidth ) {
32488             var firstItem = this.bricks.first();
32489             Roo.log(firstItem);
32490             this.columnWidth  = this.containerWidth;
32491             if (firstItem && firstItem.attr('originalwidth') ) {
32492                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32493             }
32494             // columnWidth fall back to item of first element
32495             Roo.log("set column width?");
32496                         this.initialColumnWidth = this.columnWidth  ;
32497
32498             // if first elem has no width, default to size of container
32499             
32500         }
32501         
32502         
32503         if (this.initialColumnWidth) {
32504             this.columnWidth = this.initialColumnWidth;
32505         }
32506         
32507         
32508             
32509         // column width is fixed at the top - however if container width get's smaller we should
32510         // reduce it...
32511         
32512         // this bit calcs how man columns..
32513             
32514         var columnWidth = this.columnWidth += this.gutter;
32515       
32516         // calculate columns
32517         var containerWidth = this.containerWidth + this.gutter;
32518         
32519         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32520         // fix rounding errors, typically with gutters
32521         var excess = columnWidth - containerWidth % columnWidth;
32522         
32523         
32524         // if overshoot is less than a pixel, round up, otherwise floor it
32525         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32526         cols = Math[ mathMethod ]( cols );
32527         this.cols = Math.max( cols, 1 );
32528         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32529         
32530          // padding positioning..
32531         var totalColWidth = this.cols * this.columnWidth;
32532         var padavail = this.containerWidth - totalColWidth;
32533         // so for 2 columns - we need 3 'pads'
32534         
32535         var padNeeded = (1+this.cols) * this.padWidth;
32536         
32537         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32538         
32539         this.columnWidth += padExtra
32540         //this.padWidth = Math.floor(padavail /  ( this.cols));
32541         
32542         // adjust colum width so that padding is fixed??
32543         
32544         // we have 3 columns ... total = width * 3
32545         // we have X left over... that should be used by 
32546         
32547         //if (this.expandC) {
32548             
32549         //}
32550         
32551         
32552         
32553     },
32554     
32555     getContainerWidth : function()
32556     {
32557        /* // container is parent if fit width
32558         var container = this.isFitWidth ? this.element.parentNode : this.element;
32559         // check that this.size and size are there
32560         // IE8 triggers resize on body size change, so they might not be
32561         
32562         var size = getSize( container );  //FIXME
32563         this.containerWidth = size && size.innerWidth; //FIXME
32564         */
32565          
32566         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32567         
32568     },
32569     
32570     _getItemLayoutPosition : function( item )  // what is item?
32571     {
32572         // we resize the item to our columnWidth..
32573       
32574         item.setWidth(this.columnWidth);
32575         item.autoBoxAdjust  = false;
32576         
32577         var sz = item.getSize();
32578  
32579         // how many columns does this brick span
32580         var remainder = this.containerWidth % this.columnWidth;
32581         
32582         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32583         // round if off by 1 pixel, otherwise use ceil
32584         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32585         colSpan = Math.min( colSpan, this.cols );
32586         
32587         // normally this should be '1' as we dont' currently allow multi width columns..
32588         
32589         var colGroup = this._getColGroup( colSpan );
32590         // get the minimum Y value from the columns
32591         var minimumY = Math.min.apply( Math, colGroup );
32592         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32593         
32594         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32595          
32596         // position the brick
32597         var position = {
32598             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32599             y: this.currentSize.y + minimumY + this.padHeight
32600         };
32601         
32602         Roo.log(position);
32603         // apply setHeight to necessary columns
32604         var setHeight = minimumY + sz.height + this.padHeight;
32605         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32606         
32607         var setSpan = this.cols + 1 - colGroup.length;
32608         for ( var i = 0; i < setSpan; i++ ) {
32609           this.colYs[ shortColIndex + i ] = setHeight ;
32610         }
32611       
32612         return position;
32613     },
32614     
32615     /**
32616      * @param {Number} colSpan - number of columns the element spans
32617      * @returns {Array} colGroup
32618      */
32619     _getColGroup : function( colSpan )
32620     {
32621         if ( colSpan < 2 ) {
32622           // if brick spans only one column, use all the column Ys
32623           return this.colYs;
32624         }
32625       
32626         var colGroup = [];
32627         // how many different places could this brick fit horizontally
32628         var groupCount = this.cols + 1 - colSpan;
32629         // for each group potential horizontal position
32630         for ( var i = 0; i < groupCount; i++ ) {
32631           // make an array of colY values for that one group
32632           var groupColYs = this.colYs.slice( i, i + colSpan );
32633           // and get the max value of the array
32634           colGroup[i] = Math.max.apply( Math, groupColYs );
32635         }
32636         return colGroup;
32637     },
32638     /*
32639     _manageStamp : function( stamp )
32640     {
32641         var stampSize =  stamp.getSize();
32642         var offset = stamp.getBox();
32643         // get the columns that this stamp affects
32644         var firstX = this.isOriginLeft ? offset.x : offset.right;
32645         var lastX = firstX + stampSize.width;
32646         var firstCol = Math.floor( firstX / this.columnWidth );
32647         firstCol = Math.max( 0, firstCol );
32648         
32649         var lastCol = Math.floor( lastX / this.columnWidth );
32650         // lastCol should not go over if multiple of columnWidth #425
32651         lastCol -= lastX % this.columnWidth ? 0 : 1;
32652         lastCol = Math.min( this.cols - 1, lastCol );
32653         
32654         // set colYs to bottom of the stamp
32655         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32656             stampSize.height;
32657             
32658         for ( var i = firstCol; i <= lastCol; i++ ) {
32659           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32660         }
32661     },
32662     */
32663     
32664     _getContainerSize : function()
32665     {
32666         this.maxY = Math.max.apply( Math, this.colYs );
32667         var size = {
32668             height: this.maxY
32669         };
32670       
32671         if ( this.isFitWidth ) {
32672             size.width = this._getContainerFitWidth();
32673         }
32674       
32675         return size;
32676     },
32677     
32678     _getContainerFitWidth : function()
32679     {
32680         var unusedCols = 0;
32681         // count unused columns
32682         var i = this.cols;
32683         while ( --i ) {
32684           if ( this.colYs[i] !== 0 ) {
32685             break;
32686           }
32687           unusedCols++;
32688         }
32689         // fit container to columns that have been used
32690         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32691     },
32692     
32693     needsResizeLayout : function()
32694     {
32695         var previousWidth = this.containerWidth;
32696         this.getContainerWidth();
32697         return previousWidth !== this.containerWidth;
32698     }
32699  
32700 });
32701
32702  
32703
32704  /*
32705  * - LGPL
32706  *
32707  * element
32708  * 
32709  */
32710
32711 /**
32712  * @class Roo.bootstrap.MasonryBrick
32713  * @extends Roo.bootstrap.Component
32714  * Bootstrap MasonryBrick class
32715  * 
32716  * @constructor
32717  * Create a new MasonryBrick
32718  * @param {Object} config The config object
32719  */
32720
32721 Roo.bootstrap.MasonryBrick = function(config){
32722     
32723     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32724     
32725     Roo.bootstrap.MasonryBrick.register(this);
32726     
32727     this.addEvents({
32728         // raw events
32729         /**
32730          * @event click
32731          * When a MasonryBrick is clcik
32732          * @param {Roo.bootstrap.MasonryBrick} this
32733          * @param {Roo.EventObject} e
32734          */
32735         "click" : true
32736     });
32737 };
32738
32739 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32740     
32741     /**
32742      * @cfg {String} title
32743      */   
32744     title : '',
32745     /**
32746      * @cfg {String} html
32747      */   
32748     html : '',
32749     /**
32750      * @cfg {String} bgimage
32751      */   
32752     bgimage : '',
32753     /**
32754      * @cfg {String} videourl
32755      */   
32756     videourl : '',
32757     /**
32758      * @cfg {String} cls
32759      */   
32760     cls : '',
32761     /**
32762      * @cfg {String} href
32763      */   
32764     href : '',
32765     /**
32766      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32767      */   
32768     size : 'xs',
32769     
32770     /**
32771      * @cfg {String} placetitle (center|bottom)
32772      */   
32773     placetitle : '',
32774     
32775     /**
32776      * @cfg {Boolean} isFitContainer defalut true
32777      */   
32778     isFitContainer : true, 
32779     
32780     /**
32781      * @cfg {Boolean} preventDefault defalut false
32782      */   
32783     preventDefault : false, 
32784     
32785     /**
32786      * @cfg {Boolean} inverse defalut false
32787      */   
32788     maskInverse : false, 
32789     
32790     getAutoCreate : function()
32791     {
32792         if(!this.isFitContainer){
32793             return this.getSplitAutoCreate();
32794         }
32795         
32796         var cls = 'masonry-brick masonry-brick-full';
32797         
32798         if(this.href.length){
32799             cls += ' masonry-brick-link';
32800         }
32801         
32802         if(this.bgimage.length){
32803             cls += ' masonry-brick-image';
32804         }
32805         
32806         if(this.maskInverse){
32807             cls += ' mask-inverse';
32808         }
32809         
32810         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32811             cls += ' enable-mask';
32812         }
32813         
32814         if(this.size){
32815             cls += ' masonry-' + this.size + '-brick';
32816         }
32817         
32818         if(this.placetitle.length){
32819             
32820             switch (this.placetitle) {
32821                 case 'center' :
32822                     cls += ' masonry-center-title';
32823                     break;
32824                 case 'bottom' :
32825                     cls += ' masonry-bottom-title';
32826                     break;
32827                 default:
32828                     break;
32829             }
32830             
32831         } else {
32832             if(!this.html.length && !this.bgimage.length){
32833                 cls += ' masonry-center-title';
32834             }
32835
32836             if(!this.html.length && this.bgimage.length){
32837                 cls += ' masonry-bottom-title';
32838             }
32839         }
32840         
32841         if(this.cls){
32842             cls += ' ' + this.cls;
32843         }
32844         
32845         var cfg = {
32846             tag: (this.href.length) ? 'a' : 'div',
32847             cls: cls,
32848             cn: [
32849                 {
32850                     tag: 'div',
32851                     cls: 'masonry-brick-mask'
32852                 },
32853                 {
32854                     tag: 'div',
32855                     cls: 'masonry-brick-paragraph',
32856                     cn: []
32857                 }
32858             ]
32859         };
32860         
32861         if(this.href.length){
32862             cfg.href = this.href;
32863         }
32864         
32865         var cn = cfg.cn[1].cn;
32866         
32867         if(this.title.length){
32868             cn.push({
32869                 tag: 'h4',
32870                 cls: 'masonry-brick-title',
32871                 html: this.title
32872             });
32873         }
32874         
32875         if(this.html.length){
32876             cn.push({
32877                 tag: 'p',
32878                 cls: 'masonry-brick-text',
32879                 html: this.html
32880             });
32881         }
32882         
32883         if (!this.title.length && !this.html.length) {
32884             cfg.cn[1].cls += ' hide';
32885         }
32886         
32887         if(this.bgimage.length){
32888             cfg.cn.push({
32889                 tag: 'img',
32890                 cls: 'masonry-brick-image-view',
32891                 src: this.bgimage
32892             });
32893         }
32894         
32895         if(this.videourl.length){
32896             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32897             // youtube support only?
32898             cfg.cn.push({
32899                 tag: 'iframe',
32900                 cls: 'masonry-brick-image-view',
32901                 src: vurl,
32902                 frameborder : 0,
32903                 allowfullscreen : true
32904             });
32905         }
32906         
32907         return cfg;
32908         
32909     },
32910     
32911     getSplitAutoCreate : function()
32912     {
32913         var cls = 'masonry-brick masonry-brick-split';
32914         
32915         if(this.href.length){
32916             cls += ' masonry-brick-link';
32917         }
32918         
32919         if(this.bgimage.length){
32920             cls += ' masonry-brick-image';
32921         }
32922         
32923         if(this.size){
32924             cls += ' masonry-' + this.size + '-brick';
32925         }
32926         
32927         switch (this.placetitle) {
32928             case 'center' :
32929                 cls += ' masonry-center-title';
32930                 break;
32931             case 'bottom' :
32932                 cls += ' masonry-bottom-title';
32933                 break;
32934             default:
32935                 if(!this.bgimage.length){
32936                     cls += ' masonry-center-title';
32937                 }
32938
32939                 if(this.bgimage.length){
32940                     cls += ' masonry-bottom-title';
32941                 }
32942                 break;
32943         }
32944         
32945         if(this.cls){
32946             cls += ' ' + this.cls;
32947         }
32948         
32949         var cfg = {
32950             tag: (this.href.length) ? 'a' : 'div',
32951             cls: cls,
32952             cn: [
32953                 {
32954                     tag: 'div',
32955                     cls: 'masonry-brick-split-head',
32956                     cn: [
32957                         {
32958                             tag: 'div',
32959                             cls: 'masonry-brick-paragraph',
32960                             cn: []
32961                         }
32962                     ]
32963                 },
32964                 {
32965                     tag: 'div',
32966                     cls: 'masonry-brick-split-body',
32967                     cn: []
32968                 }
32969             ]
32970         };
32971         
32972         if(this.href.length){
32973             cfg.href = this.href;
32974         }
32975         
32976         if(this.title.length){
32977             cfg.cn[0].cn[0].cn.push({
32978                 tag: 'h4',
32979                 cls: 'masonry-brick-title',
32980                 html: this.title
32981             });
32982         }
32983         
32984         if(this.html.length){
32985             cfg.cn[1].cn.push({
32986                 tag: 'p',
32987                 cls: 'masonry-brick-text',
32988                 html: this.html
32989             });
32990         }
32991
32992         if(this.bgimage.length){
32993             cfg.cn[0].cn.push({
32994                 tag: 'img',
32995                 cls: 'masonry-brick-image-view',
32996                 src: this.bgimage
32997             });
32998         }
32999         
33000         if(this.videourl.length){
33001             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33002             // youtube support only?
33003             cfg.cn[0].cn.cn.push({
33004                 tag: 'iframe',
33005                 cls: 'masonry-brick-image-view',
33006                 src: vurl,
33007                 frameborder : 0,
33008                 allowfullscreen : true
33009             });
33010         }
33011         
33012         return cfg;
33013     },
33014     
33015     initEvents: function() 
33016     {
33017         switch (this.size) {
33018             case 'xs' :
33019                 this.x = 1;
33020                 this.y = 1;
33021                 break;
33022             case 'sm' :
33023                 this.x = 2;
33024                 this.y = 2;
33025                 break;
33026             case 'md' :
33027             case 'md-left' :
33028             case 'md-right' :
33029                 this.x = 3;
33030                 this.y = 3;
33031                 break;
33032             case 'tall' :
33033                 this.x = 2;
33034                 this.y = 3;
33035                 break;
33036             case 'wide' :
33037                 this.x = 3;
33038                 this.y = 2;
33039                 break;
33040             case 'wide-thin' :
33041                 this.x = 3;
33042                 this.y = 1;
33043                 break;
33044                         
33045             default :
33046                 break;
33047         }
33048         
33049         if(Roo.isTouch){
33050             this.el.on('touchstart', this.onTouchStart, this);
33051             this.el.on('touchmove', this.onTouchMove, this);
33052             this.el.on('touchend', this.onTouchEnd, this);
33053             this.el.on('contextmenu', this.onContextMenu, this);
33054         } else {
33055             this.el.on('mouseenter'  ,this.enter, this);
33056             this.el.on('mouseleave', this.leave, this);
33057             this.el.on('click', this.onClick, this);
33058         }
33059         
33060         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33061             this.parent().bricks.push(this);   
33062         }
33063         
33064     },
33065     
33066     onClick: function(e, el)
33067     {
33068         var time = this.endTimer - this.startTimer;
33069         // Roo.log(e.preventDefault());
33070         if(Roo.isTouch){
33071             if(time > 1000){
33072                 e.preventDefault();
33073                 return;
33074             }
33075         }
33076         
33077         if(!this.preventDefault){
33078             return;
33079         }
33080         
33081         e.preventDefault();
33082         
33083         if (this.activeClass != '') {
33084             this.selectBrick();
33085         }
33086         
33087         this.fireEvent('click', this, e);
33088     },
33089     
33090     enter: function(e, el)
33091     {
33092         e.preventDefault();
33093         
33094         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33095             return;
33096         }
33097         
33098         if(this.bgimage.length && this.html.length){
33099             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33100         }
33101     },
33102     
33103     leave: function(e, el)
33104     {
33105         e.preventDefault();
33106         
33107         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33108             return;
33109         }
33110         
33111         if(this.bgimage.length && this.html.length){
33112             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33113         }
33114     },
33115     
33116     onTouchStart: function(e, el)
33117     {
33118 //        e.preventDefault();
33119         
33120         this.touchmoved = false;
33121         
33122         if(!this.isFitContainer){
33123             return;
33124         }
33125         
33126         if(!this.bgimage.length || !this.html.length){
33127             return;
33128         }
33129         
33130         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33131         
33132         this.timer = new Date().getTime();
33133         
33134     },
33135     
33136     onTouchMove: function(e, el)
33137     {
33138         this.touchmoved = true;
33139     },
33140     
33141     onContextMenu : function(e,el)
33142     {
33143         e.preventDefault();
33144         e.stopPropagation();
33145         return false;
33146     },
33147     
33148     onTouchEnd: function(e, el)
33149     {
33150 //        e.preventDefault();
33151         
33152         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33153         
33154             this.leave(e,el);
33155             
33156             return;
33157         }
33158         
33159         if(!this.bgimage.length || !this.html.length){
33160             
33161             if(this.href.length){
33162                 window.location.href = this.href;
33163             }
33164             
33165             return;
33166         }
33167         
33168         if(!this.isFitContainer){
33169             return;
33170         }
33171         
33172         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33173         
33174         window.location.href = this.href;
33175     },
33176     
33177     //selection on single brick only
33178     selectBrick : function() {
33179         
33180         if (!this.parentId) {
33181             return;
33182         }
33183         
33184         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33185         var index = m.selectedBrick.indexOf(this.id);
33186         
33187         if ( index > -1) {
33188             m.selectedBrick.splice(index,1);
33189             this.el.removeClass(this.activeClass);
33190             return;
33191         }
33192         
33193         for(var i = 0; i < m.selectedBrick.length; i++) {
33194             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33195             b.el.removeClass(b.activeClass);
33196         }
33197         
33198         m.selectedBrick = [];
33199         
33200         m.selectedBrick.push(this.id);
33201         this.el.addClass(this.activeClass);
33202         return;
33203     },
33204     
33205     isSelected : function(){
33206         return this.el.hasClass(this.activeClass);
33207         
33208     }
33209 });
33210
33211 Roo.apply(Roo.bootstrap.MasonryBrick, {
33212     
33213     //groups: {},
33214     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33215      /**
33216     * register a Masonry Brick
33217     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33218     */
33219     
33220     register : function(brick)
33221     {
33222         //this.groups[brick.id] = brick;
33223         this.groups.add(brick.id, brick);
33224     },
33225     /**
33226     * fetch a  masonry brick based on the masonry brick ID
33227     * @param {string} the masonry brick to add
33228     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33229     */
33230     
33231     get: function(brick_id) 
33232     {
33233         // if (typeof(this.groups[brick_id]) == 'undefined') {
33234         //     return false;
33235         // }
33236         // return this.groups[brick_id] ;
33237         
33238         if(this.groups.key(brick_id)) {
33239             return this.groups.key(brick_id);
33240         }
33241         
33242         return false;
33243     }
33244     
33245     
33246     
33247 });
33248
33249  /*
33250  * - LGPL
33251  *
33252  * element
33253  * 
33254  */
33255
33256 /**
33257  * @class Roo.bootstrap.Brick
33258  * @extends Roo.bootstrap.Component
33259  * Bootstrap Brick class
33260  * 
33261  * @constructor
33262  * Create a new Brick
33263  * @param {Object} config The config object
33264  */
33265
33266 Roo.bootstrap.Brick = function(config){
33267     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33268     
33269     this.addEvents({
33270         // raw events
33271         /**
33272          * @event click
33273          * When a Brick is click
33274          * @param {Roo.bootstrap.Brick} this
33275          * @param {Roo.EventObject} e
33276          */
33277         "click" : true
33278     });
33279 };
33280
33281 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33282     
33283     /**
33284      * @cfg {String} title
33285      */   
33286     title : '',
33287     /**
33288      * @cfg {String} html
33289      */   
33290     html : '',
33291     /**
33292      * @cfg {String} bgimage
33293      */   
33294     bgimage : '',
33295     /**
33296      * @cfg {String} cls
33297      */   
33298     cls : '',
33299     /**
33300      * @cfg {String} href
33301      */   
33302     href : '',
33303     /**
33304      * @cfg {String} video
33305      */   
33306     video : '',
33307     /**
33308      * @cfg {Boolean} square
33309      */   
33310     square : true,
33311     
33312     getAutoCreate : function()
33313     {
33314         var cls = 'roo-brick';
33315         
33316         if(this.href.length){
33317             cls += ' roo-brick-link';
33318         }
33319         
33320         if(this.bgimage.length){
33321             cls += ' roo-brick-image';
33322         }
33323         
33324         if(!this.html.length && !this.bgimage.length){
33325             cls += ' roo-brick-center-title';
33326         }
33327         
33328         if(!this.html.length && this.bgimage.length){
33329             cls += ' roo-brick-bottom-title';
33330         }
33331         
33332         if(this.cls){
33333             cls += ' ' + this.cls;
33334         }
33335         
33336         var cfg = {
33337             tag: (this.href.length) ? 'a' : 'div',
33338             cls: cls,
33339             cn: [
33340                 {
33341                     tag: 'div',
33342                     cls: 'roo-brick-paragraph',
33343                     cn: []
33344                 }
33345             ]
33346         };
33347         
33348         if(this.href.length){
33349             cfg.href = this.href;
33350         }
33351         
33352         var cn = cfg.cn[0].cn;
33353         
33354         if(this.title.length){
33355             cn.push({
33356                 tag: 'h4',
33357                 cls: 'roo-brick-title',
33358                 html: this.title
33359             });
33360         }
33361         
33362         if(this.html.length){
33363             cn.push({
33364                 tag: 'p',
33365                 cls: 'roo-brick-text',
33366                 html: this.html
33367             });
33368         } else {
33369             cn.cls += ' hide';
33370         }
33371         
33372         if(this.bgimage.length){
33373             cfg.cn.push({
33374                 tag: 'img',
33375                 cls: 'roo-brick-image-view',
33376                 src: this.bgimage
33377             });
33378         }
33379         
33380         return cfg;
33381     },
33382     
33383     initEvents: function() 
33384     {
33385         if(this.title.length || this.html.length){
33386             this.el.on('mouseenter'  ,this.enter, this);
33387             this.el.on('mouseleave', this.leave, this);
33388         }
33389         
33390         Roo.EventManager.onWindowResize(this.resize, this); 
33391         
33392         if(this.bgimage.length){
33393             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33394             this.imageEl.on('load', this.onImageLoad, this);
33395             return;
33396         }
33397         
33398         this.resize();
33399     },
33400     
33401     onImageLoad : function()
33402     {
33403         this.resize();
33404     },
33405     
33406     resize : function()
33407     {
33408         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33409         
33410         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33411         
33412         if(this.bgimage.length){
33413             var image = this.el.select('.roo-brick-image-view', true).first();
33414             
33415             image.setWidth(paragraph.getWidth());
33416             
33417             if(this.square){
33418                 image.setHeight(paragraph.getWidth());
33419             }
33420             
33421             this.el.setHeight(image.getHeight());
33422             paragraph.setHeight(image.getHeight());
33423             
33424         }
33425         
33426     },
33427     
33428     enter: function(e, el)
33429     {
33430         e.preventDefault();
33431         
33432         if(this.bgimage.length){
33433             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33434             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33435         }
33436     },
33437     
33438     leave: function(e, el)
33439     {
33440         e.preventDefault();
33441         
33442         if(this.bgimage.length){
33443             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33444             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33445         }
33446     }
33447     
33448 });
33449
33450  
33451
33452  /*
33453  * - LGPL
33454  *
33455  * Number field 
33456  */
33457
33458 /**
33459  * @class Roo.bootstrap.NumberField
33460  * @extends Roo.bootstrap.Input
33461  * Bootstrap NumberField class
33462  * 
33463  * 
33464  * 
33465  * 
33466  * @constructor
33467  * Create a new NumberField
33468  * @param {Object} config The config object
33469  */
33470
33471 Roo.bootstrap.NumberField = function(config){
33472     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33473 };
33474
33475 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33476     
33477     /**
33478      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33479      */
33480     allowDecimals : true,
33481     /**
33482      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33483      */
33484     decimalSeparator : ".",
33485     /**
33486      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33487      */
33488     decimalPrecision : 2,
33489     /**
33490      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33491      */
33492     allowNegative : true,
33493     
33494     /**
33495      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33496      */
33497     allowZero: true,
33498     /**
33499      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33500      */
33501     minValue : Number.NEGATIVE_INFINITY,
33502     /**
33503      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33504      */
33505     maxValue : Number.MAX_VALUE,
33506     /**
33507      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33508      */
33509     minText : "The minimum value for this field is {0}",
33510     /**
33511      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33512      */
33513     maxText : "The maximum value for this field is {0}",
33514     /**
33515      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33516      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33517      */
33518     nanText : "{0} is not a valid number",
33519     /**
33520      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33521      */
33522     thousandsDelimiter : false,
33523     /**
33524      * @cfg {String} valueAlign alignment of value
33525      */
33526     valueAlign : "left",
33527
33528     getAutoCreate : function()
33529     {
33530         var hiddenInput = {
33531             tag: 'input',
33532             type: 'hidden',
33533             id: Roo.id(),
33534             cls: 'hidden-number-input'
33535         };
33536         
33537         if (this.name) {
33538             hiddenInput.name = this.name;
33539         }
33540         
33541         this.name = '';
33542         
33543         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33544         
33545         this.name = hiddenInput.name;
33546         
33547         if(cfg.cn.length > 0) {
33548             cfg.cn.push(hiddenInput);
33549         }
33550         
33551         return cfg;
33552     },
33553
33554     // private
33555     initEvents : function()
33556     {   
33557         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33558         
33559         var allowed = "0123456789";
33560         
33561         if(this.allowDecimals){
33562             allowed += this.decimalSeparator;
33563         }
33564         
33565         if(this.allowNegative){
33566             allowed += "-";
33567         }
33568         
33569         if(this.thousandsDelimiter) {
33570             allowed += ",";
33571         }
33572         
33573         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33574         
33575         var keyPress = function(e){
33576             
33577             var k = e.getKey();
33578             
33579             var c = e.getCharCode();
33580             
33581             if(
33582                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33583                     allowed.indexOf(String.fromCharCode(c)) === -1
33584             ){
33585                 e.stopEvent();
33586                 return;
33587             }
33588             
33589             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33590                 return;
33591             }
33592             
33593             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33594                 e.stopEvent();
33595             }
33596         };
33597         
33598         this.el.on("keypress", keyPress, this);
33599     },
33600     
33601     validateValue : function(value)
33602     {
33603         
33604         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33605             return false;
33606         }
33607         
33608         var num = this.parseValue(value);
33609         
33610         if(isNaN(num)){
33611             this.markInvalid(String.format(this.nanText, value));
33612             return false;
33613         }
33614         
33615         if(num < this.minValue){
33616             this.markInvalid(String.format(this.minText, this.minValue));
33617             return false;
33618         }
33619         
33620         if(num > this.maxValue){
33621             this.markInvalid(String.format(this.maxText, this.maxValue));
33622             return false;
33623         }
33624         
33625         return true;
33626     },
33627
33628     getValue : function()
33629     {
33630         var v = this.hiddenEl().getValue();
33631         
33632         return this.fixPrecision(this.parseValue(v));
33633     },
33634
33635     parseValue : function(value)
33636     {
33637         if(this.thousandsDelimiter) {
33638             value += "";
33639             r = new RegExp(",", "g");
33640             value = value.replace(r, "");
33641         }
33642         
33643         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33644         return isNaN(value) ? '' : value;
33645     },
33646
33647     fixPrecision : function(value)
33648     {
33649         if(this.thousandsDelimiter) {
33650             value += "";
33651             r = new RegExp(",", "g");
33652             value = value.replace(r, "");
33653         }
33654         
33655         var nan = isNaN(value);
33656         
33657         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33658             return nan ? '' : value;
33659         }
33660         return parseFloat(value).toFixed(this.decimalPrecision);
33661     },
33662
33663     setValue : function(v)
33664     {
33665         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33666         
33667         this.value = v;
33668         
33669         if(this.rendered){
33670             
33671             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33672             
33673             this.inputEl().dom.value = (v == '') ? '' :
33674                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33675             
33676             if(!this.allowZero && v === '0') {
33677                 this.hiddenEl().dom.value = '';
33678                 this.inputEl().dom.value = '';
33679             }
33680             
33681             this.validate();
33682         }
33683     },
33684
33685     decimalPrecisionFcn : function(v)
33686     {
33687         return Math.floor(v);
33688     },
33689
33690     beforeBlur : function()
33691     {
33692         var v = this.parseValue(this.getRawValue());
33693         
33694         if(v || v === 0 || v === ''){
33695             this.setValue(v);
33696         }
33697     },
33698     
33699     hiddenEl : function()
33700     {
33701         return this.el.select('input.hidden-number-input',true).first();
33702     }
33703     
33704 });
33705
33706  
33707
33708 /*
33709 * Licence: LGPL
33710 */
33711
33712 /**
33713  * @class Roo.bootstrap.DocumentSlider
33714  * @extends Roo.bootstrap.Component
33715  * Bootstrap DocumentSlider class
33716  * 
33717  * @constructor
33718  * Create a new DocumentViewer
33719  * @param {Object} config The config object
33720  */
33721
33722 Roo.bootstrap.DocumentSlider = function(config){
33723     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33724     
33725     this.files = [];
33726     
33727     this.addEvents({
33728         /**
33729          * @event initial
33730          * Fire after initEvent
33731          * @param {Roo.bootstrap.DocumentSlider} this
33732          */
33733         "initial" : true,
33734         /**
33735          * @event update
33736          * Fire after update
33737          * @param {Roo.bootstrap.DocumentSlider} this
33738          */
33739         "update" : true,
33740         /**
33741          * @event click
33742          * Fire after click
33743          * @param {Roo.bootstrap.DocumentSlider} this
33744          */
33745         "click" : true
33746     });
33747 };
33748
33749 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33750     
33751     files : false,
33752     
33753     indicator : 0,
33754     
33755     getAutoCreate : function()
33756     {
33757         var cfg = {
33758             tag : 'div',
33759             cls : 'roo-document-slider',
33760             cn : [
33761                 {
33762                     tag : 'div',
33763                     cls : 'roo-document-slider-header',
33764                     cn : [
33765                         {
33766                             tag : 'div',
33767                             cls : 'roo-document-slider-header-title'
33768                         }
33769                     ]
33770                 },
33771                 {
33772                     tag : 'div',
33773                     cls : 'roo-document-slider-body',
33774                     cn : [
33775                         {
33776                             tag : 'div',
33777                             cls : 'roo-document-slider-prev',
33778                             cn : [
33779                                 {
33780                                     tag : 'i',
33781                                     cls : 'fa fa-chevron-left'
33782                                 }
33783                             ]
33784                         },
33785                         {
33786                             tag : 'div',
33787                             cls : 'roo-document-slider-thumb',
33788                             cn : [
33789                                 {
33790                                     tag : 'img',
33791                                     cls : 'roo-document-slider-image'
33792                                 }
33793                             ]
33794                         },
33795                         {
33796                             tag : 'div',
33797                             cls : 'roo-document-slider-next',
33798                             cn : [
33799                                 {
33800                                     tag : 'i',
33801                                     cls : 'fa fa-chevron-right'
33802                                 }
33803                             ]
33804                         }
33805                     ]
33806                 }
33807             ]
33808         };
33809         
33810         return cfg;
33811     },
33812     
33813     initEvents : function()
33814     {
33815         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33816         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33817         
33818         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33819         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33820         
33821         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33822         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33823         
33824         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33825         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33826         
33827         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33828         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33829         
33830         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33831         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33832         
33833         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33834         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33835         
33836         this.thumbEl.on('click', this.onClick, this);
33837         
33838         this.prevIndicator.on('click', this.prev, this);
33839         
33840         this.nextIndicator.on('click', this.next, this);
33841         
33842     },
33843     
33844     initial : function()
33845     {
33846         if(this.files.length){
33847             this.indicator = 1;
33848             this.update()
33849         }
33850         
33851         this.fireEvent('initial', this);
33852     },
33853     
33854     update : function()
33855     {
33856         this.imageEl.attr('src', this.files[this.indicator - 1]);
33857         
33858         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33859         
33860         this.prevIndicator.show();
33861         
33862         if(this.indicator == 1){
33863             this.prevIndicator.hide();
33864         }
33865         
33866         this.nextIndicator.show();
33867         
33868         if(this.indicator == this.files.length){
33869             this.nextIndicator.hide();
33870         }
33871         
33872         this.thumbEl.scrollTo('top');
33873         
33874         this.fireEvent('update', this);
33875     },
33876     
33877     onClick : function(e)
33878     {
33879         e.preventDefault();
33880         
33881         this.fireEvent('click', this);
33882     },
33883     
33884     prev : function(e)
33885     {
33886         e.preventDefault();
33887         
33888         this.indicator = Math.max(1, this.indicator - 1);
33889         
33890         this.update();
33891     },
33892     
33893     next : function(e)
33894     {
33895         e.preventDefault();
33896         
33897         this.indicator = Math.min(this.files.length, this.indicator + 1);
33898         
33899         this.update();
33900     }
33901 });
33902 /*
33903  * - LGPL
33904  *
33905  * RadioSet
33906  *
33907  *
33908  */
33909
33910 /**
33911  * @class Roo.bootstrap.RadioSet
33912  * @extends Roo.bootstrap.Input
33913  * Bootstrap RadioSet class
33914  * @cfg {String} indicatorpos (left|right) default left
33915  * @cfg {Boolean} inline (true|false) inline the element (default true)
33916  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33917  * @constructor
33918  * Create a new RadioSet
33919  * @param {Object} config The config object
33920  */
33921
33922 Roo.bootstrap.RadioSet = function(config){
33923     
33924     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33925     
33926     this.radioes = [];
33927     
33928     Roo.bootstrap.RadioSet.register(this);
33929     
33930     this.addEvents({
33931         /**
33932         * @event check
33933         * Fires when the element is checked or unchecked.
33934         * @param {Roo.bootstrap.RadioSet} this This radio
33935         * @param {Roo.bootstrap.Radio} item The checked item
33936         */
33937        check : true,
33938        /**
33939         * @event click
33940         * Fires when the element is click.
33941         * @param {Roo.bootstrap.RadioSet} this This radio set
33942         * @param {Roo.bootstrap.Radio} item The checked item
33943         * @param {Roo.EventObject} e The event object
33944         */
33945        click : true
33946     });
33947     
33948 };
33949
33950 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33951
33952     radioes : false,
33953     
33954     inline : true,
33955     
33956     weight : '',
33957     
33958     indicatorpos : 'left',
33959     
33960     getAutoCreate : function()
33961     {
33962         var label = {
33963             tag : 'label',
33964             cls : 'roo-radio-set-label',
33965             cn : [
33966                 {
33967                     tag : 'span',
33968                     html : this.fieldLabel
33969                 }
33970             ]
33971         };
33972         
33973         if(this.indicatorpos == 'left'){
33974             label.cn.unshift({
33975                 tag : 'i',
33976                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33977                 tooltip : 'This field is required'
33978             });
33979         } else {
33980             label.cn.push({
33981                 tag : 'i',
33982                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33983                 tooltip : 'This field is required'
33984             });
33985         }
33986         
33987         var items = {
33988             tag : 'div',
33989             cls : 'roo-radio-set-items'
33990         };
33991         
33992         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33993         
33994         if (align === 'left' && this.fieldLabel.length) {
33995             
33996             items = {
33997                 cls : "roo-radio-set-right", 
33998                 cn: [
33999                     items
34000                 ]
34001             };
34002             
34003             if(this.labelWidth > 12){
34004                 label.style = "width: " + this.labelWidth + 'px';
34005             }
34006             
34007             if(this.labelWidth < 13 && this.labelmd == 0){
34008                 this.labelmd = this.labelWidth;
34009             }
34010             
34011             if(this.labellg > 0){
34012                 label.cls += ' col-lg-' + this.labellg;
34013                 items.cls += ' col-lg-' + (12 - this.labellg);
34014             }
34015             
34016             if(this.labelmd > 0){
34017                 label.cls += ' col-md-' + this.labelmd;
34018                 items.cls += ' col-md-' + (12 - this.labelmd);
34019             }
34020             
34021             if(this.labelsm > 0){
34022                 label.cls += ' col-sm-' + this.labelsm;
34023                 items.cls += ' col-sm-' + (12 - this.labelsm);
34024             }
34025             
34026             if(this.labelxs > 0){
34027                 label.cls += ' col-xs-' + this.labelxs;
34028                 items.cls += ' col-xs-' + (12 - this.labelxs);
34029             }
34030         }
34031         
34032         var cfg = {
34033             tag : 'div',
34034             cls : 'roo-radio-set',
34035             cn : [
34036                 {
34037                     tag : 'input',
34038                     cls : 'roo-radio-set-input',
34039                     type : 'hidden',
34040                     name : this.name,
34041                     value : this.value ? this.value :  ''
34042                 },
34043                 label,
34044                 items
34045             ]
34046         };
34047         
34048         if(this.weight.length){
34049             cfg.cls += ' roo-radio-' + this.weight;
34050         }
34051         
34052         if(this.inline) {
34053             cfg.cls += ' roo-radio-set-inline';
34054         }
34055         
34056         var settings=this;
34057         ['xs','sm','md','lg'].map(function(size){
34058             if (settings[size]) {
34059                 cfg.cls += ' col-' + size + '-' + settings[size];
34060             }
34061         });
34062         
34063         return cfg;
34064         
34065     },
34066
34067     initEvents : function()
34068     {
34069         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34070         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34071         
34072         if(!this.fieldLabel.length){
34073             this.labelEl.hide();
34074         }
34075         
34076         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34077         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34078         
34079         this.indicator = this.indicatorEl();
34080         
34081         if(this.indicator){
34082             this.indicator.addClass('invisible');
34083         }
34084         
34085         this.originalValue = this.getValue();
34086         
34087     },
34088     
34089     inputEl: function ()
34090     {
34091         return this.el.select('.roo-radio-set-input', true).first();
34092     },
34093     
34094     getChildContainer : function()
34095     {
34096         return this.itemsEl;
34097     },
34098     
34099     register : function(item)
34100     {
34101         this.radioes.push(item);
34102         
34103     },
34104     
34105     validate : function()
34106     {   
34107         if(this.getVisibilityEl().hasClass('hidden')){
34108             return true;
34109         }
34110         
34111         var valid = false;
34112         
34113         Roo.each(this.radioes, function(i){
34114             if(!i.checked){
34115                 return;
34116             }
34117             
34118             valid = true;
34119             return false;
34120         });
34121         
34122         if(this.allowBlank) {
34123             return true;
34124         }
34125         
34126         if(this.disabled || valid){
34127             this.markValid();
34128             return true;
34129         }
34130         
34131         this.markInvalid();
34132         return false;
34133         
34134     },
34135     
34136     markValid : function()
34137     {
34138         if(this.labelEl.isVisible(true)){
34139             this.indicatorEl().removeClass('visible');
34140             this.indicatorEl().addClass('invisible');
34141         }
34142         
34143         this.el.removeClass([this.invalidClass, this.validClass]);
34144         this.el.addClass(this.validClass);
34145         
34146         this.fireEvent('valid', this);
34147     },
34148     
34149     markInvalid : function(msg)
34150     {
34151         if(this.allowBlank || this.disabled){
34152             return;
34153         }
34154         
34155         if(this.labelEl.isVisible(true)){
34156             this.indicatorEl().removeClass('invisible');
34157             this.indicatorEl().addClass('visible');
34158         }
34159         
34160         this.el.removeClass([this.invalidClass, this.validClass]);
34161         this.el.addClass(this.invalidClass);
34162         
34163         this.fireEvent('invalid', this, msg);
34164         
34165     },
34166     
34167     setValue : function(v, suppressEvent)
34168     {   
34169         if(this.value === v){
34170             return;
34171         }
34172         
34173         this.value = v;
34174         
34175         if(this.rendered){
34176             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34177         }
34178         
34179         Roo.each(this.radioes, function(i){
34180             i.checked = false;
34181             i.el.removeClass('checked');
34182         });
34183         
34184         Roo.each(this.radioes, function(i){
34185             
34186             if(i.value === v || i.value.toString() === v.toString()){
34187                 i.checked = true;
34188                 i.el.addClass('checked');
34189                 
34190                 if(suppressEvent !== true){
34191                     this.fireEvent('check', this, i);
34192                 }
34193                 
34194                 return false;
34195             }
34196             
34197         }, this);
34198         
34199         this.validate();
34200     },
34201     
34202     clearInvalid : function(){
34203         
34204         if(!this.el || this.preventMark){
34205             return;
34206         }
34207         
34208         this.el.removeClass([this.invalidClass]);
34209         
34210         this.fireEvent('valid', this);
34211     }
34212     
34213 });
34214
34215 Roo.apply(Roo.bootstrap.RadioSet, {
34216     
34217     groups: {},
34218     
34219     register : function(set)
34220     {
34221         this.groups[set.name] = set;
34222     },
34223     
34224     get: function(name) 
34225     {
34226         if (typeof(this.groups[name]) == 'undefined') {
34227             return false;
34228         }
34229         
34230         return this.groups[name] ;
34231     }
34232     
34233 });
34234 /*
34235  * Based on:
34236  * Ext JS Library 1.1.1
34237  * Copyright(c) 2006-2007, Ext JS, LLC.
34238  *
34239  * Originally Released Under LGPL - original licence link has changed is not relivant.
34240  *
34241  * Fork - LGPL
34242  * <script type="text/javascript">
34243  */
34244
34245
34246 /**
34247  * @class Roo.bootstrap.SplitBar
34248  * @extends Roo.util.Observable
34249  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34250  * <br><br>
34251  * Usage:
34252  * <pre><code>
34253 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34254                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34255 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34256 split.minSize = 100;
34257 split.maxSize = 600;
34258 split.animate = true;
34259 split.on('moved', splitterMoved);
34260 </code></pre>
34261  * @constructor
34262  * Create a new SplitBar
34263  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34264  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34265  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34266  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34267                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34268                         position of the SplitBar).
34269  */
34270 Roo.bootstrap.SplitBar = function(cfg){
34271     
34272     /** @private */
34273     
34274     //{
34275     //  dragElement : elm
34276     //  resizingElement: el,
34277         // optional..
34278     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34279     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34280         // existingProxy ???
34281     //}
34282     
34283     this.el = Roo.get(cfg.dragElement, true);
34284     this.el.dom.unselectable = "on";
34285     /** @private */
34286     this.resizingEl = Roo.get(cfg.resizingElement, true);
34287
34288     /**
34289      * @private
34290      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34291      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34292      * @type Number
34293      */
34294     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34295     
34296     /**
34297      * The minimum size of the resizing element. (Defaults to 0)
34298      * @type Number
34299      */
34300     this.minSize = 0;
34301     
34302     /**
34303      * The maximum size of the resizing element. (Defaults to 2000)
34304      * @type Number
34305      */
34306     this.maxSize = 2000;
34307     
34308     /**
34309      * Whether to animate the transition to the new size
34310      * @type Boolean
34311      */
34312     this.animate = false;
34313     
34314     /**
34315      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34316      * @type Boolean
34317      */
34318     this.useShim = false;
34319     
34320     /** @private */
34321     this.shim = null;
34322     
34323     if(!cfg.existingProxy){
34324         /** @private */
34325         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34326     }else{
34327         this.proxy = Roo.get(cfg.existingProxy).dom;
34328     }
34329     /** @private */
34330     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34331     
34332     /** @private */
34333     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34334     
34335     /** @private */
34336     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34337     
34338     /** @private */
34339     this.dragSpecs = {};
34340     
34341     /**
34342      * @private The adapter to use to positon and resize elements
34343      */
34344     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34345     this.adapter.init(this);
34346     
34347     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34348         /** @private */
34349         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34350         this.el.addClass("roo-splitbar-h");
34351     }else{
34352         /** @private */
34353         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34354         this.el.addClass("roo-splitbar-v");
34355     }
34356     
34357     this.addEvents({
34358         /**
34359          * @event resize
34360          * Fires when the splitter is moved (alias for {@link #event-moved})
34361          * @param {Roo.bootstrap.SplitBar} this
34362          * @param {Number} newSize the new width or height
34363          */
34364         "resize" : true,
34365         /**
34366          * @event moved
34367          * Fires when the splitter is moved
34368          * @param {Roo.bootstrap.SplitBar} this
34369          * @param {Number} newSize the new width or height
34370          */
34371         "moved" : true,
34372         /**
34373          * @event beforeresize
34374          * Fires before the splitter is dragged
34375          * @param {Roo.bootstrap.SplitBar} this
34376          */
34377         "beforeresize" : true,
34378
34379         "beforeapply" : true
34380     });
34381
34382     Roo.util.Observable.call(this);
34383 };
34384
34385 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34386     onStartProxyDrag : function(x, y){
34387         this.fireEvent("beforeresize", this);
34388         if(!this.overlay){
34389             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34390             o.unselectable();
34391             o.enableDisplayMode("block");
34392             // all splitbars share the same overlay
34393             Roo.bootstrap.SplitBar.prototype.overlay = o;
34394         }
34395         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34396         this.overlay.show();
34397         Roo.get(this.proxy).setDisplayed("block");
34398         var size = this.adapter.getElementSize(this);
34399         this.activeMinSize = this.getMinimumSize();;
34400         this.activeMaxSize = this.getMaximumSize();;
34401         var c1 = size - this.activeMinSize;
34402         var c2 = Math.max(this.activeMaxSize - size, 0);
34403         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34404             this.dd.resetConstraints();
34405             this.dd.setXConstraint(
34406                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34407                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34408             );
34409             this.dd.setYConstraint(0, 0);
34410         }else{
34411             this.dd.resetConstraints();
34412             this.dd.setXConstraint(0, 0);
34413             this.dd.setYConstraint(
34414                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34415                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34416             );
34417          }
34418         this.dragSpecs.startSize = size;
34419         this.dragSpecs.startPoint = [x, y];
34420         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34421     },
34422     
34423     /** 
34424      * @private Called after the drag operation by the DDProxy
34425      */
34426     onEndProxyDrag : function(e){
34427         Roo.get(this.proxy).setDisplayed(false);
34428         var endPoint = Roo.lib.Event.getXY(e);
34429         if(this.overlay){
34430             this.overlay.hide();
34431         }
34432         var newSize;
34433         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34434             newSize = this.dragSpecs.startSize + 
34435                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34436                     endPoint[0] - this.dragSpecs.startPoint[0] :
34437                     this.dragSpecs.startPoint[0] - endPoint[0]
34438                 );
34439         }else{
34440             newSize = this.dragSpecs.startSize + 
34441                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34442                     endPoint[1] - this.dragSpecs.startPoint[1] :
34443                     this.dragSpecs.startPoint[1] - endPoint[1]
34444                 );
34445         }
34446         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34447         if(newSize != this.dragSpecs.startSize){
34448             if(this.fireEvent('beforeapply', this, newSize) !== false){
34449                 this.adapter.setElementSize(this, newSize);
34450                 this.fireEvent("moved", this, newSize);
34451                 this.fireEvent("resize", this, newSize);
34452             }
34453         }
34454     },
34455     
34456     /**
34457      * Get the adapter this SplitBar uses
34458      * @return The adapter object
34459      */
34460     getAdapter : function(){
34461         return this.adapter;
34462     },
34463     
34464     /**
34465      * Set the adapter this SplitBar uses
34466      * @param {Object} adapter A SplitBar adapter object
34467      */
34468     setAdapter : function(adapter){
34469         this.adapter = adapter;
34470         this.adapter.init(this);
34471     },
34472     
34473     /**
34474      * Gets the minimum size for the resizing element
34475      * @return {Number} The minimum size
34476      */
34477     getMinimumSize : function(){
34478         return this.minSize;
34479     },
34480     
34481     /**
34482      * Sets the minimum size for the resizing element
34483      * @param {Number} minSize The minimum size
34484      */
34485     setMinimumSize : function(minSize){
34486         this.minSize = minSize;
34487     },
34488     
34489     /**
34490      * Gets the maximum size for the resizing element
34491      * @return {Number} The maximum size
34492      */
34493     getMaximumSize : function(){
34494         return this.maxSize;
34495     },
34496     
34497     /**
34498      * Sets the maximum size for the resizing element
34499      * @param {Number} maxSize The maximum size
34500      */
34501     setMaximumSize : function(maxSize){
34502         this.maxSize = maxSize;
34503     },
34504     
34505     /**
34506      * Sets the initialize size for the resizing element
34507      * @param {Number} size The initial size
34508      */
34509     setCurrentSize : function(size){
34510         var oldAnimate = this.animate;
34511         this.animate = false;
34512         this.adapter.setElementSize(this, size);
34513         this.animate = oldAnimate;
34514     },
34515     
34516     /**
34517      * Destroy this splitbar. 
34518      * @param {Boolean} removeEl True to remove the element
34519      */
34520     destroy : function(removeEl){
34521         if(this.shim){
34522             this.shim.remove();
34523         }
34524         this.dd.unreg();
34525         this.proxy.parentNode.removeChild(this.proxy);
34526         if(removeEl){
34527             this.el.remove();
34528         }
34529     }
34530 });
34531
34532 /**
34533  * @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.
34534  */
34535 Roo.bootstrap.SplitBar.createProxy = function(dir){
34536     var proxy = new Roo.Element(document.createElement("div"));
34537     proxy.unselectable();
34538     var cls = 'roo-splitbar-proxy';
34539     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34540     document.body.appendChild(proxy.dom);
34541     return proxy.dom;
34542 };
34543
34544 /** 
34545  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34546  * Default Adapter. It assumes the splitter and resizing element are not positioned
34547  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34548  */
34549 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34550 };
34551
34552 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34553     // do nothing for now
34554     init : function(s){
34555     
34556     },
34557     /**
34558      * Called before drag operations to get the current size of the resizing element. 
34559      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34560      */
34561      getElementSize : function(s){
34562         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34563             return s.resizingEl.getWidth();
34564         }else{
34565             return s.resizingEl.getHeight();
34566         }
34567     },
34568     
34569     /**
34570      * Called after drag operations to set the size of the resizing element.
34571      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34572      * @param {Number} newSize The new size to set
34573      * @param {Function} onComplete A function to be invoked when resizing is complete
34574      */
34575     setElementSize : function(s, newSize, onComplete){
34576         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34577             if(!s.animate){
34578                 s.resizingEl.setWidth(newSize);
34579                 if(onComplete){
34580                     onComplete(s, newSize);
34581                 }
34582             }else{
34583                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34584             }
34585         }else{
34586             
34587             if(!s.animate){
34588                 s.resizingEl.setHeight(newSize);
34589                 if(onComplete){
34590                     onComplete(s, newSize);
34591                 }
34592             }else{
34593                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34594             }
34595         }
34596     }
34597 };
34598
34599 /** 
34600  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34601  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34602  * Adapter that  moves the splitter element to align with the resized sizing element. 
34603  * Used with an absolute positioned SplitBar.
34604  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34605  * document.body, make sure you assign an id to the body element.
34606  */
34607 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34608     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34609     this.container = Roo.get(container);
34610 };
34611
34612 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34613     init : function(s){
34614         this.basic.init(s);
34615     },
34616     
34617     getElementSize : function(s){
34618         return this.basic.getElementSize(s);
34619     },
34620     
34621     setElementSize : function(s, newSize, onComplete){
34622         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34623     },
34624     
34625     moveSplitter : function(s){
34626         var yes = Roo.bootstrap.SplitBar;
34627         switch(s.placement){
34628             case yes.LEFT:
34629                 s.el.setX(s.resizingEl.getRight());
34630                 break;
34631             case yes.RIGHT:
34632                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34633                 break;
34634             case yes.TOP:
34635                 s.el.setY(s.resizingEl.getBottom());
34636                 break;
34637             case yes.BOTTOM:
34638                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34639                 break;
34640         }
34641     }
34642 };
34643
34644 /**
34645  * Orientation constant - Create a vertical SplitBar
34646  * @static
34647  * @type Number
34648  */
34649 Roo.bootstrap.SplitBar.VERTICAL = 1;
34650
34651 /**
34652  * Orientation constant - Create a horizontal SplitBar
34653  * @static
34654  * @type Number
34655  */
34656 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34657
34658 /**
34659  * Placement constant - The resizing element is to the left of the splitter element
34660  * @static
34661  * @type Number
34662  */
34663 Roo.bootstrap.SplitBar.LEFT = 1;
34664
34665 /**
34666  * Placement constant - The resizing element is to the right of the splitter element
34667  * @static
34668  * @type Number
34669  */
34670 Roo.bootstrap.SplitBar.RIGHT = 2;
34671
34672 /**
34673  * Placement constant - The resizing element is positioned above the splitter element
34674  * @static
34675  * @type Number
34676  */
34677 Roo.bootstrap.SplitBar.TOP = 3;
34678
34679 /**
34680  * Placement constant - The resizing element is positioned under splitter element
34681  * @static
34682  * @type Number
34683  */
34684 Roo.bootstrap.SplitBar.BOTTOM = 4;
34685 Roo.namespace("Roo.bootstrap.layout");/*
34686  * Based on:
34687  * Ext JS Library 1.1.1
34688  * Copyright(c) 2006-2007, Ext JS, LLC.
34689  *
34690  * Originally Released Under LGPL - original licence link has changed is not relivant.
34691  *
34692  * Fork - LGPL
34693  * <script type="text/javascript">
34694  */
34695
34696 /**
34697  * @class Roo.bootstrap.layout.Manager
34698  * @extends Roo.bootstrap.Component
34699  * Base class for layout managers.
34700  */
34701 Roo.bootstrap.layout.Manager = function(config)
34702 {
34703     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34704
34705
34706
34707
34708
34709     /** false to disable window resize monitoring @type Boolean */
34710     this.monitorWindowResize = true;
34711     this.regions = {};
34712     this.addEvents({
34713         /**
34714          * @event layout
34715          * Fires when a layout is performed.
34716          * @param {Roo.LayoutManager} this
34717          */
34718         "layout" : true,
34719         /**
34720          * @event regionresized
34721          * Fires when the user resizes a region.
34722          * @param {Roo.LayoutRegion} region The resized region
34723          * @param {Number} newSize The new size (width for east/west, height for north/south)
34724          */
34725         "regionresized" : true,
34726         /**
34727          * @event regioncollapsed
34728          * Fires when a region is collapsed.
34729          * @param {Roo.LayoutRegion} region The collapsed region
34730          */
34731         "regioncollapsed" : true,
34732         /**
34733          * @event regionexpanded
34734          * Fires when a region is expanded.
34735          * @param {Roo.LayoutRegion} region The expanded region
34736          */
34737         "regionexpanded" : true
34738     });
34739     this.updating = false;
34740
34741     if (config.el) {
34742         this.el = Roo.get(config.el);
34743         this.initEvents();
34744     }
34745
34746 };
34747
34748 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34749
34750
34751     regions : null,
34752
34753     monitorWindowResize : true,
34754
34755
34756     updating : false,
34757
34758
34759     onRender : function(ct, position)
34760     {
34761         if(!this.el){
34762             this.el = Roo.get(ct);
34763             this.initEvents();
34764         }
34765         //this.fireEvent('render',this);
34766     },
34767
34768
34769     initEvents: function()
34770     {
34771
34772
34773         // ie scrollbar fix
34774         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34775             document.body.scroll = "no";
34776         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34777             this.el.position('relative');
34778         }
34779         this.id = this.el.id;
34780         this.el.addClass("roo-layout-container");
34781         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34782         if(this.el.dom != document.body ) {
34783             this.el.on('resize', this.layout,this);
34784             this.el.on('show', this.layout,this);
34785         }
34786
34787     },
34788
34789     /**
34790      * Returns true if this layout is currently being updated
34791      * @return {Boolean}
34792      */
34793     isUpdating : function(){
34794         return this.updating;
34795     },
34796
34797     /**
34798      * Suspend the LayoutManager from doing auto-layouts while
34799      * making multiple add or remove calls
34800      */
34801     beginUpdate : function(){
34802         this.updating = true;
34803     },
34804
34805     /**
34806      * Restore auto-layouts and optionally disable the manager from performing a layout
34807      * @param {Boolean} noLayout true to disable a layout update
34808      */
34809     endUpdate : function(noLayout){
34810         this.updating = false;
34811         if(!noLayout){
34812             this.layout();
34813         }
34814     },
34815
34816     layout: function(){
34817         // abstract...
34818     },
34819
34820     onRegionResized : function(region, newSize){
34821         this.fireEvent("regionresized", region, newSize);
34822         this.layout();
34823     },
34824
34825     onRegionCollapsed : function(region){
34826         this.fireEvent("regioncollapsed", region);
34827     },
34828
34829     onRegionExpanded : function(region){
34830         this.fireEvent("regionexpanded", region);
34831     },
34832
34833     /**
34834      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34835      * performs box-model adjustments.
34836      * @return {Object} The size as an object {width: (the width), height: (the height)}
34837      */
34838     getViewSize : function()
34839     {
34840         var size;
34841         if(this.el.dom != document.body){
34842             size = this.el.getSize();
34843         }else{
34844             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34845         }
34846         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34847         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34848         return size;
34849     },
34850
34851     /**
34852      * Returns the Element this layout is bound to.
34853      * @return {Roo.Element}
34854      */
34855     getEl : function(){
34856         return this.el;
34857     },
34858
34859     /**
34860      * Returns the specified region.
34861      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34862      * @return {Roo.LayoutRegion}
34863      */
34864     getRegion : function(target){
34865         return this.regions[target.toLowerCase()];
34866     },
34867
34868     onWindowResize : function(){
34869         if(this.monitorWindowResize){
34870             this.layout();
34871         }
34872     }
34873 });
34874 /*
34875  * Based on:
34876  * Ext JS Library 1.1.1
34877  * Copyright(c) 2006-2007, Ext JS, LLC.
34878  *
34879  * Originally Released Under LGPL - original licence link has changed is not relivant.
34880  *
34881  * Fork - LGPL
34882  * <script type="text/javascript">
34883  */
34884 /**
34885  * @class Roo.bootstrap.layout.Border
34886  * @extends Roo.bootstrap.layout.Manager
34887  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34888  * please see: examples/bootstrap/nested.html<br><br>
34889  
34890 <b>The container the layout is rendered into can be either the body element or any other element.
34891 If it is not the body element, the container needs to either be an absolute positioned element,
34892 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34893 the container size if it is not the body element.</b>
34894
34895 * @constructor
34896 * Create a new Border
34897 * @param {Object} config Configuration options
34898  */
34899 Roo.bootstrap.layout.Border = function(config){
34900     config = config || {};
34901     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34902     
34903     
34904     
34905     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34906         if(config[region]){
34907             config[region].region = region;
34908             this.addRegion(config[region]);
34909         }
34910     },this);
34911     
34912 };
34913
34914 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34915
34916 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34917     /**
34918      * Creates and adds a new region if it doesn't already exist.
34919      * @param {String} target The target region key (north, south, east, west or center).
34920      * @param {Object} config The regions config object
34921      * @return {BorderLayoutRegion} The new region
34922      */
34923     addRegion : function(config)
34924     {
34925         if(!this.regions[config.region]){
34926             var r = this.factory(config);
34927             this.bindRegion(r);
34928         }
34929         return this.regions[config.region];
34930     },
34931
34932     // private (kinda)
34933     bindRegion : function(r){
34934         this.regions[r.config.region] = r;
34935         
34936         r.on("visibilitychange",    this.layout, this);
34937         r.on("paneladded",          this.layout, this);
34938         r.on("panelremoved",        this.layout, this);
34939         r.on("invalidated",         this.layout, this);
34940         r.on("resized",             this.onRegionResized, this);
34941         r.on("collapsed",           this.onRegionCollapsed, this);
34942         r.on("expanded",            this.onRegionExpanded, this);
34943     },
34944
34945     /**
34946      * Performs a layout update.
34947      */
34948     layout : function()
34949     {
34950         if(this.updating) {
34951             return;
34952         }
34953         
34954         // render all the rebions if they have not been done alreayd?
34955         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34956             if(this.regions[region] && !this.regions[region].bodyEl){
34957                 this.regions[region].onRender(this.el)
34958             }
34959         },this);
34960         
34961         var size = this.getViewSize();
34962         var w = size.width;
34963         var h = size.height;
34964         var centerW = w;
34965         var centerH = h;
34966         var centerY = 0;
34967         var centerX = 0;
34968         //var x = 0, y = 0;
34969
34970         var rs = this.regions;
34971         var north = rs["north"];
34972         var south = rs["south"]; 
34973         var west = rs["west"];
34974         var east = rs["east"];
34975         var center = rs["center"];
34976         //if(this.hideOnLayout){ // not supported anymore
34977             //c.el.setStyle("display", "none");
34978         //}
34979         if(north && north.isVisible()){
34980             var b = north.getBox();
34981             var m = north.getMargins();
34982             b.width = w - (m.left+m.right);
34983             b.x = m.left;
34984             b.y = m.top;
34985             centerY = b.height + b.y + m.bottom;
34986             centerH -= centerY;
34987             north.updateBox(this.safeBox(b));
34988         }
34989         if(south && south.isVisible()){
34990             var b = south.getBox();
34991             var m = south.getMargins();
34992             b.width = w - (m.left+m.right);
34993             b.x = m.left;
34994             var totalHeight = (b.height + m.top + m.bottom);
34995             b.y = h - totalHeight + m.top;
34996             centerH -= totalHeight;
34997             south.updateBox(this.safeBox(b));
34998         }
34999         if(west && west.isVisible()){
35000             var b = west.getBox();
35001             var m = west.getMargins();
35002             b.height = centerH - (m.top+m.bottom);
35003             b.x = m.left;
35004             b.y = centerY + m.top;
35005             var totalWidth = (b.width + m.left + m.right);
35006             centerX += totalWidth;
35007             centerW -= totalWidth;
35008             west.updateBox(this.safeBox(b));
35009         }
35010         if(east && east.isVisible()){
35011             var b = east.getBox();
35012             var m = east.getMargins();
35013             b.height = centerH - (m.top+m.bottom);
35014             var totalWidth = (b.width + m.left + m.right);
35015             b.x = w - totalWidth + m.left;
35016             b.y = centerY + m.top;
35017             centerW -= totalWidth;
35018             east.updateBox(this.safeBox(b));
35019         }
35020         if(center){
35021             var m = center.getMargins();
35022             var centerBox = {
35023                 x: centerX + m.left,
35024                 y: centerY + m.top,
35025                 width: centerW - (m.left+m.right),
35026                 height: centerH - (m.top+m.bottom)
35027             };
35028             //if(this.hideOnLayout){
35029                 //center.el.setStyle("display", "block");
35030             //}
35031             center.updateBox(this.safeBox(centerBox));
35032         }
35033         this.el.repaint();
35034         this.fireEvent("layout", this);
35035     },
35036
35037     // private
35038     safeBox : function(box){
35039         box.width = Math.max(0, box.width);
35040         box.height = Math.max(0, box.height);
35041         return box;
35042     },
35043
35044     /**
35045      * Adds a ContentPanel (or subclass) to this layout.
35046      * @param {String} target The target region key (north, south, east, west or center).
35047      * @param {Roo.ContentPanel} panel The panel to add
35048      * @return {Roo.ContentPanel} The added panel
35049      */
35050     add : function(target, panel){
35051          
35052         target = target.toLowerCase();
35053         return this.regions[target].add(panel);
35054     },
35055
35056     /**
35057      * Remove a ContentPanel (or subclass) to this layout.
35058      * @param {String} target The target region key (north, south, east, west or center).
35059      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35060      * @return {Roo.ContentPanel} The removed panel
35061      */
35062     remove : function(target, panel){
35063         target = target.toLowerCase();
35064         return this.regions[target].remove(panel);
35065     },
35066
35067     /**
35068      * Searches all regions for a panel with the specified id
35069      * @param {String} panelId
35070      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35071      */
35072     findPanel : function(panelId){
35073         var rs = this.regions;
35074         for(var target in rs){
35075             if(typeof rs[target] != "function"){
35076                 var p = rs[target].getPanel(panelId);
35077                 if(p){
35078                     return p;
35079                 }
35080             }
35081         }
35082         return null;
35083     },
35084
35085     /**
35086      * Searches all regions for a panel with the specified id and activates (shows) it.
35087      * @param {String/ContentPanel} panelId The panels id or the panel itself
35088      * @return {Roo.ContentPanel} The shown panel or null
35089      */
35090     showPanel : function(panelId) {
35091       var rs = this.regions;
35092       for(var target in rs){
35093          var r = rs[target];
35094          if(typeof r != "function"){
35095             if(r.hasPanel(panelId)){
35096                return r.showPanel(panelId);
35097             }
35098          }
35099       }
35100       return null;
35101    },
35102
35103    /**
35104      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35105      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35106      */
35107    /*
35108     restoreState : function(provider){
35109         if(!provider){
35110             provider = Roo.state.Manager;
35111         }
35112         var sm = new Roo.LayoutStateManager();
35113         sm.init(this, provider);
35114     },
35115 */
35116  
35117  
35118     /**
35119      * Adds a xtype elements to the layout.
35120      * <pre><code>
35121
35122 layout.addxtype({
35123        xtype : 'ContentPanel',
35124        region: 'west',
35125        items: [ .... ]
35126    }
35127 );
35128
35129 layout.addxtype({
35130         xtype : 'NestedLayoutPanel',
35131         region: 'west',
35132         layout: {
35133            center: { },
35134            west: { }   
35135         },
35136         items : [ ... list of content panels or nested layout panels.. ]
35137    }
35138 );
35139 </code></pre>
35140      * @param {Object} cfg Xtype definition of item to add.
35141      */
35142     addxtype : function(cfg)
35143     {
35144         // basically accepts a pannel...
35145         // can accept a layout region..!?!?
35146         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35147         
35148         
35149         // theory?  children can only be panels??
35150         
35151         //if (!cfg.xtype.match(/Panel$/)) {
35152         //    return false;
35153         //}
35154         var ret = false;
35155         
35156         if (typeof(cfg.region) == 'undefined') {
35157             Roo.log("Failed to add Panel, region was not set");
35158             Roo.log(cfg);
35159             return false;
35160         }
35161         var region = cfg.region;
35162         delete cfg.region;
35163         
35164           
35165         var xitems = [];
35166         if (cfg.items) {
35167             xitems = cfg.items;
35168             delete cfg.items;
35169         }
35170         var nb = false;
35171         
35172         switch(cfg.xtype) 
35173         {
35174             case 'Content':  // ContentPanel (el, cfg)
35175             case 'Scroll':  // ContentPanel (el, cfg)
35176             case 'View': 
35177                 cfg.autoCreate = true;
35178                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35179                 //} else {
35180                 //    var el = this.el.createChild();
35181                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35182                 //}
35183                 
35184                 this.add(region, ret);
35185                 break;
35186             
35187             /*
35188             case 'TreePanel': // our new panel!
35189                 cfg.el = this.el.createChild();
35190                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35191                 this.add(region, ret);
35192                 break;
35193             */
35194             
35195             case 'Nest': 
35196                 // create a new Layout (which is  a Border Layout...
35197                 
35198                 var clayout = cfg.layout;
35199                 clayout.el  = this.el.createChild();
35200                 clayout.items   = clayout.items  || [];
35201                 
35202                 delete cfg.layout;
35203                 
35204                 // replace this exitems with the clayout ones..
35205                 xitems = clayout.items;
35206                  
35207                 // force background off if it's in center...
35208                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35209                     cfg.background = false;
35210                 }
35211                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35212                 
35213                 
35214                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35215                 //console.log('adding nested layout panel '  + cfg.toSource());
35216                 this.add(region, ret);
35217                 nb = {}; /// find first...
35218                 break;
35219             
35220             case 'Grid':
35221                 
35222                 // needs grid and region
35223                 
35224                 //var el = this.getRegion(region).el.createChild();
35225                 /*
35226                  *var el = this.el.createChild();
35227                 // create the grid first...
35228                 cfg.grid.container = el;
35229                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35230                 */
35231                 
35232                 if (region == 'center' && this.active ) {
35233                     cfg.background = false;
35234                 }
35235                 
35236                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35237                 
35238                 this.add(region, ret);
35239                 /*
35240                 if (cfg.background) {
35241                     // render grid on panel activation (if panel background)
35242                     ret.on('activate', function(gp) {
35243                         if (!gp.grid.rendered) {
35244                     //        gp.grid.render(el);
35245                         }
35246                     });
35247                 } else {
35248                   //  cfg.grid.render(el);
35249                 }
35250                 */
35251                 break;
35252            
35253            
35254             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35255                 // it was the old xcomponent building that caused this before.
35256                 // espeically if border is the top element in the tree.
35257                 ret = this;
35258                 break; 
35259                 
35260                     
35261                 
35262                 
35263                 
35264             default:
35265                 /*
35266                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35267                     
35268                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35269                     this.add(region, ret);
35270                 } else {
35271                 */
35272                     Roo.log(cfg);
35273                     throw "Can not add '" + cfg.xtype + "' to Border";
35274                     return null;
35275              
35276                                 
35277              
35278         }
35279         this.beginUpdate();
35280         // add children..
35281         var region = '';
35282         var abn = {};
35283         Roo.each(xitems, function(i)  {
35284             region = nb && i.region ? i.region : false;
35285             
35286             var add = ret.addxtype(i);
35287            
35288             if (region) {
35289                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35290                 if (!i.background) {
35291                     abn[region] = nb[region] ;
35292                 }
35293             }
35294             
35295         });
35296         this.endUpdate();
35297
35298         // make the last non-background panel active..
35299         //if (nb) { Roo.log(abn); }
35300         if (nb) {
35301             
35302             for(var r in abn) {
35303                 region = this.getRegion(r);
35304                 if (region) {
35305                     // tried using nb[r], but it does not work..
35306                      
35307                     region.showPanel(abn[r]);
35308                    
35309                 }
35310             }
35311         }
35312         return ret;
35313         
35314     },
35315     
35316     
35317 // private
35318     factory : function(cfg)
35319     {
35320         
35321         var validRegions = Roo.bootstrap.layout.Border.regions;
35322
35323         var target = cfg.region;
35324         cfg.mgr = this;
35325         
35326         var r = Roo.bootstrap.layout;
35327         Roo.log(target);
35328         switch(target){
35329             case "north":
35330                 return new r.North(cfg);
35331             case "south":
35332                 return new r.South(cfg);
35333             case "east":
35334                 return new r.East(cfg);
35335             case "west":
35336                 return new r.West(cfg);
35337             case "center":
35338                 return new r.Center(cfg);
35339         }
35340         throw 'Layout region "'+target+'" not supported.';
35341     }
35342     
35343     
35344 });
35345  /*
35346  * Based on:
35347  * Ext JS Library 1.1.1
35348  * Copyright(c) 2006-2007, Ext JS, LLC.
35349  *
35350  * Originally Released Under LGPL - original licence link has changed is not relivant.
35351  *
35352  * Fork - LGPL
35353  * <script type="text/javascript">
35354  */
35355  
35356 /**
35357  * @class Roo.bootstrap.layout.Basic
35358  * @extends Roo.util.Observable
35359  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35360  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35361  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35362  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35363  * @cfg {string}   region  the region that it inhabits..
35364  * @cfg {bool}   skipConfig skip config?
35365  * 
35366
35367  */
35368 Roo.bootstrap.layout.Basic = function(config){
35369     
35370     this.mgr = config.mgr;
35371     
35372     this.position = config.region;
35373     
35374     var skipConfig = config.skipConfig;
35375     
35376     this.events = {
35377         /**
35378          * @scope Roo.BasicLayoutRegion
35379          */
35380         
35381         /**
35382          * @event beforeremove
35383          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35384          * @param {Roo.LayoutRegion} this
35385          * @param {Roo.ContentPanel} panel The panel
35386          * @param {Object} e The cancel event object
35387          */
35388         "beforeremove" : true,
35389         /**
35390          * @event invalidated
35391          * Fires when the layout for this region is changed.
35392          * @param {Roo.LayoutRegion} this
35393          */
35394         "invalidated" : true,
35395         /**
35396          * @event visibilitychange
35397          * Fires when this region is shown or hidden 
35398          * @param {Roo.LayoutRegion} this
35399          * @param {Boolean} visibility true or false
35400          */
35401         "visibilitychange" : true,
35402         /**
35403          * @event paneladded
35404          * Fires when a panel is added. 
35405          * @param {Roo.LayoutRegion} this
35406          * @param {Roo.ContentPanel} panel The panel
35407          */
35408         "paneladded" : true,
35409         /**
35410          * @event panelremoved
35411          * Fires when a panel is removed. 
35412          * @param {Roo.LayoutRegion} this
35413          * @param {Roo.ContentPanel} panel The panel
35414          */
35415         "panelremoved" : true,
35416         /**
35417          * @event beforecollapse
35418          * Fires when this region before collapse.
35419          * @param {Roo.LayoutRegion} this
35420          */
35421         "beforecollapse" : true,
35422         /**
35423          * @event collapsed
35424          * Fires when this region is collapsed.
35425          * @param {Roo.LayoutRegion} this
35426          */
35427         "collapsed" : true,
35428         /**
35429          * @event expanded
35430          * Fires when this region is expanded.
35431          * @param {Roo.LayoutRegion} this
35432          */
35433         "expanded" : true,
35434         /**
35435          * @event slideshow
35436          * Fires when this region is slid into view.
35437          * @param {Roo.LayoutRegion} this
35438          */
35439         "slideshow" : true,
35440         /**
35441          * @event slidehide
35442          * Fires when this region slides out of view. 
35443          * @param {Roo.LayoutRegion} this
35444          */
35445         "slidehide" : true,
35446         /**
35447          * @event panelactivated
35448          * Fires when a panel is activated. 
35449          * @param {Roo.LayoutRegion} this
35450          * @param {Roo.ContentPanel} panel The activated panel
35451          */
35452         "panelactivated" : true,
35453         /**
35454          * @event resized
35455          * Fires when the user resizes this region. 
35456          * @param {Roo.LayoutRegion} this
35457          * @param {Number} newSize The new size (width for east/west, height for north/south)
35458          */
35459         "resized" : true
35460     };
35461     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35462     this.panels = new Roo.util.MixedCollection();
35463     this.panels.getKey = this.getPanelId.createDelegate(this);
35464     this.box = null;
35465     this.activePanel = null;
35466     // ensure listeners are added...
35467     
35468     if (config.listeners || config.events) {
35469         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35470             listeners : config.listeners || {},
35471             events : config.events || {}
35472         });
35473     }
35474     
35475     if(skipConfig !== true){
35476         this.applyConfig(config);
35477     }
35478 };
35479
35480 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35481 {
35482     getPanelId : function(p){
35483         return p.getId();
35484     },
35485     
35486     applyConfig : function(config){
35487         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35488         this.config = config;
35489         
35490     },
35491     
35492     /**
35493      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35494      * the width, for horizontal (north, south) the height.
35495      * @param {Number} newSize The new width or height
35496      */
35497     resizeTo : function(newSize){
35498         var el = this.el ? this.el :
35499                  (this.activePanel ? this.activePanel.getEl() : null);
35500         if(el){
35501             switch(this.position){
35502                 case "east":
35503                 case "west":
35504                     el.setWidth(newSize);
35505                     this.fireEvent("resized", this, newSize);
35506                 break;
35507                 case "north":
35508                 case "south":
35509                     el.setHeight(newSize);
35510                     this.fireEvent("resized", this, newSize);
35511                 break;                
35512             }
35513         }
35514     },
35515     
35516     getBox : function(){
35517         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35518     },
35519     
35520     getMargins : function(){
35521         return this.margins;
35522     },
35523     
35524     updateBox : function(box){
35525         this.box = box;
35526         var el = this.activePanel.getEl();
35527         el.dom.style.left = box.x + "px";
35528         el.dom.style.top = box.y + "px";
35529         this.activePanel.setSize(box.width, box.height);
35530     },
35531     
35532     /**
35533      * Returns the container element for this region.
35534      * @return {Roo.Element}
35535      */
35536     getEl : function(){
35537         return this.activePanel;
35538     },
35539     
35540     /**
35541      * Returns true if this region is currently visible.
35542      * @return {Boolean}
35543      */
35544     isVisible : function(){
35545         return this.activePanel ? true : false;
35546     },
35547     
35548     setActivePanel : function(panel){
35549         panel = this.getPanel(panel);
35550         if(this.activePanel && this.activePanel != panel){
35551             this.activePanel.setActiveState(false);
35552             this.activePanel.getEl().setLeftTop(-10000,-10000);
35553         }
35554         this.activePanel = panel;
35555         panel.setActiveState(true);
35556         if(this.box){
35557             panel.setSize(this.box.width, this.box.height);
35558         }
35559         this.fireEvent("panelactivated", this, panel);
35560         this.fireEvent("invalidated");
35561     },
35562     
35563     /**
35564      * Show the specified panel.
35565      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35566      * @return {Roo.ContentPanel} The shown panel or null
35567      */
35568     showPanel : function(panel){
35569         panel = this.getPanel(panel);
35570         if(panel){
35571             this.setActivePanel(panel);
35572         }
35573         return panel;
35574     },
35575     
35576     /**
35577      * Get the active panel for this region.
35578      * @return {Roo.ContentPanel} The active panel or null
35579      */
35580     getActivePanel : function(){
35581         return this.activePanel;
35582     },
35583     
35584     /**
35585      * Add the passed ContentPanel(s)
35586      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35587      * @return {Roo.ContentPanel} The panel added (if only one was added)
35588      */
35589     add : function(panel){
35590         if(arguments.length > 1){
35591             for(var i = 0, len = arguments.length; i < len; i++) {
35592                 this.add(arguments[i]);
35593             }
35594             return null;
35595         }
35596         if(this.hasPanel(panel)){
35597             this.showPanel(panel);
35598             return panel;
35599         }
35600         var el = panel.getEl();
35601         if(el.dom.parentNode != this.mgr.el.dom){
35602             this.mgr.el.dom.appendChild(el.dom);
35603         }
35604         if(panel.setRegion){
35605             panel.setRegion(this);
35606         }
35607         this.panels.add(panel);
35608         el.setStyle("position", "absolute");
35609         if(!panel.background){
35610             this.setActivePanel(panel);
35611             if(this.config.initialSize && this.panels.getCount()==1){
35612                 this.resizeTo(this.config.initialSize);
35613             }
35614         }
35615         this.fireEvent("paneladded", this, panel);
35616         return panel;
35617     },
35618     
35619     /**
35620      * Returns true if the panel is in this region.
35621      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35622      * @return {Boolean}
35623      */
35624     hasPanel : function(panel){
35625         if(typeof panel == "object"){ // must be panel obj
35626             panel = panel.getId();
35627         }
35628         return this.getPanel(panel) ? true : false;
35629     },
35630     
35631     /**
35632      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35633      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35634      * @param {Boolean} preservePanel Overrides the config preservePanel option
35635      * @return {Roo.ContentPanel} The panel that was removed
35636      */
35637     remove : function(panel, preservePanel){
35638         panel = this.getPanel(panel);
35639         if(!panel){
35640             return null;
35641         }
35642         var e = {};
35643         this.fireEvent("beforeremove", this, panel, e);
35644         if(e.cancel === true){
35645             return null;
35646         }
35647         var panelId = panel.getId();
35648         this.panels.removeKey(panelId);
35649         return panel;
35650     },
35651     
35652     /**
35653      * Returns the panel specified or null if it's not in this region.
35654      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35655      * @return {Roo.ContentPanel}
35656      */
35657     getPanel : function(id){
35658         if(typeof id == "object"){ // must be panel obj
35659             return id;
35660         }
35661         return this.panels.get(id);
35662     },
35663     
35664     /**
35665      * Returns this regions position (north/south/east/west/center).
35666      * @return {String} 
35667      */
35668     getPosition: function(){
35669         return this.position;    
35670     }
35671 });/*
35672  * Based on:
35673  * Ext JS Library 1.1.1
35674  * Copyright(c) 2006-2007, Ext JS, LLC.
35675  *
35676  * Originally Released Under LGPL - original licence link has changed is not relivant.
35677  *
35678  * Fork - LGPL
35679  * <script type="text/javascript">
35680  */
35681  
35682 /**
35683  * @class Roo.bootstrap.layout.Region
35684  * @extends Roo.bootstrap.layout.Basic
35685  * This class represents a region in a layout manager.
35686  
35687  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35688  * @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})
35689  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35690  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35691  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35692  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35693  * @cfg {String}    title           The title for the region (overrides panel titles)
35694  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35695  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35696  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35697  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35698  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35699  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35700  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35701  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35702  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35703  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35704
35705  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35706  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35707  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35708  * @cfg {Number}    width           For East/West panels
35709  * @cfg {Number}    height          For North/South panels
35710  * @cfg {Boolean}   split           To show the splitter
35711  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35712  * 
35713  * @cfg {string}   cls             Extra CSS classes to add to region
35714  * 
35715  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35716  * @cfg {string}   region  the region that it inhabits..
35717  *
35718
35719  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35720  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35721
35722  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35723  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35724  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35725  */
35726 Roo.bootstrap.layout.Region = function(config)
35727 {
35728     this.applyConfig(config);
35729
35730     var mgr = config.mgr;
35731     var pos = config.region;
35732     config.skipConfig = true;
35733     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35734     
35735     if (mgr.el) {
35736         this.onRender(mgr.el);   
35737     }
35738      
35739     this.visible = true;
35740     this.collapsed = false;
35741     this.unrendered_panels = [];
35742 };
35743
35744 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35745
35746     position: '', // set by wrapper (eg. north/south etc..)
35747     unrendered_panels : null,  // unrendered panels.
35748     createBody : function(){
35749         /** This region's body element 
35750         * @type Roo.Element */
35751         this.bodyEl = this.el.createChild({
35752                 tag: "div",
35753                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35754         });
35755     },
35756
35757     onRender: function(ctr, pos)
35758     {
35759         var dh = Roo.DomHelper;
35760         /** This region's container element 
35761         * @type Roo.Element */
35762         this.el = dh.append(ctr.dom, {
35763                 tag: "div",
35764                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35765             }, true);
35766         /** This region's title element 
35767         * @type Roo.Element */
35768     
35769         this.titleEl = dh.append(this.el.dom,
35770             {
35771                     tag: "div",
35772                     unselectable: "on",
35773                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35774                     children:[
35775                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35776                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35777                     ]}, true);
35778         
35779         this.titleEl.enableDisplayMode();
35780         /** This region's title text element 
35781         * @type HTMLElement */
35782         this.titleTextEl = this.titleEl.dom.firstChild;
35783         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35784         /*
35785         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35786         this.closeBtn.enableDisplayMode();
35787         this.closeBtn.on("click", this.closeClicked, this);
35788         this.closeBtn.hide();
35789     */
35790         this.createBody(this.config);
35791         if(this.config.hideWhenEmpty){
35792             this.hide();
35793             this.on("paneladded", this.validateVisibility, this);
35794             this.on("panelremoved", this.validateVisibility, this);
35795         }
35796         if(this.autoScroll){
35797             this.bodyEl.setStyle("overflow", "auto");
35798         }else{
35799             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35800         }
35801         //if(c.titlebar !== false){
35802             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35803                 this.titleEl.hide();
35804             }else{
35805                 this.titleEl.show();
35806                 if(this.config.title){
35807                     this.titleTextEl.innerHTML = this.config.title;
35808                 }
35809             }
35810         //}
35811         if(this.config.collapsed){
35812             this.collapse(true);
35813         }
35814         if(this.config.hidden){
35815             this.hide();
35816         }
35817         
35818         if (this.unrendered_panels && this.unrendered_panels.length) {
35819             for (var i =0;i< this.unrendered_panels.length; i++) {
35820                 this.add(this.unrendered_panels[i]);
35821             }
35822             this.unrendered_panels = null;
35823             
35824         }
35825         
35826     },
35827     
35828     applyConfig : function(c)
35829     {
35830         /*
35831          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35832             var dh = Roo.DomHelper;
35833             if(c.titlebar !== false){
35834                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35835                 this.collapseBtn.on("click", this.collapse, this);
35836                 this.collapseBtn.enableDisplayMode();
35837                 /*
35838                 if(c.showPin === true || this.showPin){
35839                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35840                     this.stickBtn.enableDisplayMode();
35841                     this.stickBtn.on("click", this.expand, this);
35842                     this.stickBtn.hide();
35843                 }
35844                 
35845             }
35846             */
35847             /** This region's collapsed element
35848             * @type Roo.Element */
35849             /*
35850              *
35851             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35852                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35853             ]}, true);
35854             
35855             if(c.floatable !== false){
35856                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35857                this.collapsedEl.on("click", this.collapseClick, this);
35858             }
35859
35860             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35861                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35862                    id: "message", unselectable: "on", style:{"float":"left"}});
35863                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35864              }
35865             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35866             this.expandBtn.on("click", this.expand, this);
35867             
35868         }
35869         
35870         if(this.collapseBtn){
35871             this.collapseBtn.setVisible(c.collapsible == true);
35872         }
35873         
35874         this.cmargins = c.cmargins || this.cmargins ||
35875                          (this.position == "west" || this.position == "east" ?
35876                              {top: 0, left: 2, right:2, bottom: 0} :
35877                              {top: 2, left: 0, right:0, bottom: 2});
35878         */
35879         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35880         
35881         
35882         this.bottomTabs = c.tabPosition != "top";
35883         
35884         this.autoScroll = c.autoScroll || false;
35885         
35886         
35887        
35888         
35889         this.duration = c.duration || .30;
35890         this.slideDuration = c.slideDuration || .45;
35891         this.config = c;
35892        
35893     },
35894     /**
35895      * Returns true if this region is currently visible.
35896      * @return {Boolean}
35897      */
35898     isVisible : function(){
35899         return this.visible;
35900     },
35901
35902     /**
35903      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35904      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35905      */
35906     //setCollapsedTitle : function(title){
35907     //    title = title || "&#160;";
35908      //   if(this.collapsedTitleTextEl){
35909       //      this.collapsedTitleTextEl.innerHTML = title;
35910        // }
35911     //},
35912
35913     getBox : function(){
35914         var b;
35915       //  if(!this.collapsed){
35916             b = this.el.getBox(false, true);
35917        // }else{
35918           //  b = this.collapsedEl.getBox(false, true);
35919         //}
35920         return b;
35921     },
35922
35923     getMargins : function(){
35924         return this.margins;
35925         //return this.collapsed ? this.cmargins : this.margins;
35926     },
35927 /*
35928     highlight : function(){
35929         this.el.addClass("x-layout-panel-dragover");
35930     },
35931
35932     unhighlight : function(){
35933         this.el.removeClass("x-layout-panel-dragover");
35934     },
35935 */
35936     updateBox : function(box)
35937     {
35938         if (!this.bodyEl) {
35939             return; // not rendered yet..
35940         }
35941         
35942         this.box = box;
35943         if(!this.collapsed){
35944             this.el.dom.style.left = box.x + "px";
35945             this.el.dom.style.top = box.y + "px";
35946             this.updateBody(box.width, box.height);
35947         }else{
35948             this.collapsedEl.dom.style.left = box.x + "px";
35949             this.collapsedEl.dom.style.top = box.y + "px";
35950             this.collapsedEl.setSize(box.width, box.height);
35951         }
35952         if(this.tabs){
35953             this.tabs.autoSizeTabs();
35954         }
35955     },
35956
35957     updateBody : function(w, h)
35958     {
35959         if(w !== null){
35960             this.el.setWidth(w);
35961             w -= this.el.getBorderWidth("rl");
35962             if(this.config.adjustments){
35963                 w += this.config.adjustments[0];
35964             }
35965         }
35966         if(h !== null && h > 0){
35967             this.el.setHeight(h);
35968             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35969             h -= this.el.getBorderWidth("tb");
35970             if(this.config.adjustments){
35971                 h += this.config.adjustments[1];
35972             }
35973             this.bodyEl.setHeight(h);
35974             if(this.tabs){
35975                 h = this.tabs.syncHeight(h);
35976             }
35977         }
35978         if(this.panelSize){
35979             w = w !== null ? w : this.panelSize.width;
35980             h = h !== null ? h : this.panelSize.height;
35981         }
35982         if(this.activePanel){
35983             var el = this.activePanel.getEl();
35984             w = w !== null ? w : el.getWidth();
35985             h = h !== null ? h : el.getHeight();
35986             this.panelSize = {width: w, height: h};
35987             this.activePanel.setSize(w, h);
35988         }
35989         if(Roo.isIE && this.tabs){
35990             this.tabs.el.repaint();
35991         }
35992     },
35993
35994     /**
35995      * Returns the container element for this region.
35996      * @return {Roo.Element}
35997      */
35998     getEl : function(){
35999         return this.el;
36000     },
36001
36002     /**
36003      * Hides this region.
36004      */
36005     hide : function(){
36006         //if(!this.collapsed){
36007             this.el.dom.style.left = "-2000px";
36008             this.el.hide();
36009         //}else{
36010          //   this.collapsedEl.dom.style.left = "-2000px";
36011          //   this.collapsedEl.hide();
36012        // }
36013         this.visible = false;
36014         this.fireEvent("visibilitychange", this, false);
36015     },
36016
36017     /**
36018      * Shows this region if it was previously hidden.
36019      */
36020     show : function(){
36021         //if(!this.collapsed){
36022             this.el.show();
36023         //}else{
36024         //    this.collapsedEl.show();
36025        // }
36026         this.visible = true;
36027         this.fireEvent("visibilitychange", this, true);
36028     },
36029 /*
36030     closeClicked : function(){
36031         if(this.activePanel){
36032             this.remove(this.activePanel);
36033         }
36034     },
36035
36036     collapseClick : function(e){
36037         if(this.isSlid){
36038            e.stopPropagation();
36039            this.slideIn();
36040         }else{
36041            e.stopPropagation();
36042            this.slideOut();
36043         }
36044     },
36045 */
36046     /**
36047      * Collapses this region.
36048      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36049      */
36050     /*
36051     collapse : function(skipAnim, skipCheck = false){
36052         if(this.collapsed) {
36053             return;
36054         }
36055         
36056         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36057             
36058             this.collapsed = true;
36059             if(this.split){
36060                 this.split.el.hide();
36061             }
36062             if(this.config.animate && skipAnim !== true){
36063                 this.fireEvent("invalidated", this);
36064                 this.animateCollapse();
36065             }else{
36066                 this.el.setLocation(-20000,-20000);
36067                 this.el.hide();
36068                 this.collapsedEl.show();
36069                 this.fireEvent("collapsed", this);
36070                 this.fireEvent("invalidated", this);
36071             }
36072         }
36073         
36074     },
36075 */
36076     animateCollapse : function(){
36077         // overridden
36078     },
36079
36080     /**
36081      * Expands this region if it was previously collapsed.
36082      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36083      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36084      */
36085     /*
36086     expand : function(e, skipAnim){
36087         if(e) {
36088             e.stopPropagation();
36089         }
36090         if(!this.collapsed || this.el.hasActiveFx()) {
36091             return;
36092         }
36093         if(this.isSlid){
36094             this.afterSlideIn();
36095             skipAnim = true;
36096         }
36097         this.collapsed = false;
36098         if(this.config.animate && skipAnim !== true){
36099             this.animateExpand();
36100         }else{
36101             this.el.show();
36102             if(this.split){
36103                 this.split.el.show();
36104             }
36105             this.collapsedEl.setLocation(-2000,-2000);
36106             this.collapsedEl.hide();
36107             this.fireEvent("invalidated", this);
36108             this.fireEvent("expanded", this);
36109         }
36110     },
36111 */
36112     animateExpand : function(){
36113         // overridden
36114     },
36115
36116     initTabs : function()
36117     {
36118         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36119         
36120         var ts = new Roo.bootstrap.panel.Tabs({
36121                 el: this.bodyEl.dom,
36122                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36123                 disableTooltips: this.config.disableTabTips,
36124                 toolbar : this.config.toolbar
36125             });
36126         
36127         if(this.config.hideTabs){
36128             ts.stripWrap.setDisplayed(false);
36129         }
36130         this.tabs = ts;
36131         ts.resizeTabs = this.config.resizeTabs === true;
36132         ts.minTabWidth = this.config.minTabWidth || 40;
36133         ts.maxTabWidth = this.config.maxTabWidth || 250;
36134         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36135         ts.monitorResize = false;
36136         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36137         ts.bodyEl.addClass('roo-layout-tabs-body');
36138         this.panels.each(this.initPanelAsTab, this);
36139     },
36140
36141     initPanelAsTab : function(panel){
36142         var ti = this.tabs.addTab(
36143             panel.getEl().id,
36144             panel.getTitle(),
36145             null,
36146             this.config.closeOnTab && panel.isClosable(),
36147             panel.tpl
36148         );
36149         if(panel.tabTip !== undefined){
36150             ti.setTooltip(panel.tabTip);
36151         }
36152         ti.on("activate", function(){
36153               this.setActivePanel(panel);
36154         }, this);
36155         
36156         if(this.config.closeOnTab){
36157             ti.on("beforeclose", function(t, e){
36158                 e.cancel = true;
36159                 this.remove(panel);
36160             }, this);
36161         }
36162         
36163         panel.tabItem = ti;
36164         
36165         return ti;
36166     },
36167
36168     updatePanelTitle : function(panel, title)
36169     {
36170         if(this.activePanel == panel){
36171             this.updateTitle(title);
36172         }
36173         if(this.tabs){
36174             var ti = this.tabs.getTab(panel.getEl().id);
36175             ti.setText(title);
36176             if(panel.tabTip !== undefined){
36177                 ti.setTooltip(panel.tabTip);
36178             }
36179         }
36180     },
36181
36182     updateTitle : function(title){
36183         if(this.titleTextEl && !this.config.title){
36184             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36185         }
36186     },
36187
36188     setActivePanel : function(panel)
36189     {
36190         panel = this.getPanel(panel);
36191         if(this.activePanel && this.activePanel != panel){
36192             if(this.activePanel.setActiveState(false) === false){
36193                 return;
36194             }
36195         }
36196         this.activePanel = panel;
36197         panel.setActiveState(true);
36198         if(this.panelSize){
36199             panel.setSize(this.panelSize.width, this.panelSize.height);
36200         }
36201         if(this.closeBtn){
36202             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36203         }
36204         this.updateTitle(panel.getTitle());
36205         if(this.tabs){
36206             this.fireEvent("invalidated", this);
36207         }
36208         this.fireEvent("panelactivated", this, panel);
36209     },
36210
36211     /**
36212      * Shows the specified panel.
36213      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36214      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36215      */
36216     showPanel : function(panel)
36217     {
36218         panel = this.getPanel(panel);
36219         if(panel){
36220             if(this.tabs){
36221                 var tab = this.tabs.getTab(panel.getEl().id);
36222                 if(tab.isHidden()){
36223                     this.tabs.unhideTab(tab.id);
36224                 }
36225                 tab.activate();
36226             }else{
36227                 this.setActivePanel(panel);
36228             }
36229         }
36230         return panel;
36231     },
36232
36233     /**
36234      * Get the active panel for this region.
36235      * @return {Roo.ContentPanel} The active panel or null
36236      */
36237     getActivePanel : function(){
36238         return this.activePanel;
36239     },
36240
36241     validateVisibility : function(){
36242         if(this.panels.getCount() < 1){
36243             this.updateTitle("&#160;");
36244             this.closeBtn.hide();
36245             this.hide();
36246         }else{
36247             if(!this.isVisible()){
36248                 this.show();
36249             }
36250         }
36251     },
36252
36253     /**
36254      * Adds the passed ContentPanel(s) to this region.
36255      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36256      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36257      */
36258     add : function(panel)
36259     {
36260         if(arguments.length > 1){
36261             for(var i = 0, len = arguments.length; i < len; i++) {
36262                 this.add(arguments[i]);
36263             }
36264             return null;
36265         }
36266         
36267         // if we have not been rendered yet, then we can not really do much of this..
36268         if (!this.bodyEl) {
36269             this.unrendered_panels.push(panel);
36270             return panel;
36271         }
36272         
36273         
36274         
36275         
36276         if(this.hasPanel(panel)){
36277             this.showPanel(panel);
36278             return panel;
36279         }
36280         panel.setRegion(this);
36281         this.panels.add(panel);
36282        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36283             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36284             // and hide them... ???
36285             this.bodyEl.dom.appendChild(panel.getEl().dom);
36286             if(panel.background !== true){
36287                 this.setActivePanel(panel);
36288             }
36289             this.fireEvent("paneladded", this, panel);
36290             return panel;
36291         }
36292         */
36293         if(!this.tabs){
36294             this.initTabs();
36295         }else{
36296             this.initPanelAsTab(panel);
36297         }
36298         
36299         
36300         if(panel.background !== true){
36301             this.tabs.activate(panel.getEl().id);
36302         }
36303         this.fireEvent("paneladded", this, panel);
36304         return panel;
36305     },
36306
36307     /**
36308      * Hides the tab for the specified panel.
36309      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36310      */
36311     hidePanel : function(panel){
36312         if(this.tabs && (panel = this.getPanel(panel))){
36313             this.tabs.hideTab(panel.getEl().id);
36314         }
36315     },
36316
36317     /**
36318      * Unhides the tab for a previously hidden panel.
36319      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36320      */
36321     unhidePanel : function(panel){
36322         if(this.tabs && (panel = this.getPanel(panel))){
36323             this.tabs.unhideTab(panel.getEl().id);
36324         }
36325     },
36326
36327     clearPanels : function(){
36328         while(this.panels.getCount() > 0){
36329              this.remove(this.panels.first());
36330         }
36331     },
36332
36333     /**
36334      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36335      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36336      * @param {Boolean} preservePanel Overrides the config preservePanel option
36337      * @return {Roo.ContentPanel} The panel that was removed
36338      */
36339     remove : function(panel, preservePanel)
36340     {
36341         panel = this.getPanel(panel);
36342         if(!panel){
36343             return null;
36344         }
36345         var e = {};
36346         this.fireEvent("beforeremove", this, panel, e);
36347         if(e.cancel === true){
36348             return null;
36349         }
36350         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36351         var panelId = panel.getId();
36352         this.panels.removeKey(panelId);
36353         if(preservePanel){
36354             document.body.appendChild(panel.getEl().dom);
36355         }
36356         if(this.tabs){
36357             this.tabs.removeTab(panel.getEl().id);
36358         }else if (!preservePanel){
36359             this.bodyEl.dom.removeChild(panel.getEl().dom);
36360         }
36361         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36362             var p = this.panels.first();
36363             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36364             tempEl.appendChild(p.getEl().dom);
36365             this.bodyEl.update("");
36366             this.bodyEl.dom.appendChild(p.getEl().dom);
36367             tempEl = null;
36368             this.updateTitle(p.getTitle());
36369             this.tabs = null;
36370             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36371             this.setActivePanel(p);
36372         }
36373         panel.setRegion(null);
36374         if(this.activePanel == panel){
36375             this.activePanel = null;
36376         }
36377         if(this.config.autoDestroy !== false && preservePanel !== true){
36378             try{panel.destroy();}catch(e){}
36379         }
36380         this.fireEvent("panelremoved", this, panel);
36381         return panel;
36382     },
36383
36384     /**
36385      * Returns the TabPanel component used by this region
36386      * @return {Roo.TabPanel}
36387      */
36388     getTabs : function(){
36389         return this.tabs;
36390     },
36391
36392     createTool : function(parentEl, className){
36393         var btn = Roo.DomHelper.append(parentEl, {
36394             tag: "div",
36395             cls: "x-layout-tools-button",
36396             children: [ {
36397                 tag: "div",
36398                 cls: "roo-layout-tools-button-inner " + className,
36399                 html: "&#160;"
36400             }]
36401         }, true);
36402         btn.addClassOnOver("roo-layout-tools-button-over");
36403         return btn;
36404     }
36405 });/*
36406  * Based on:
36407  * Ext JS Library 1.1.1
36408  * Copyright(c) 2006-2007, Ext JS, LLC.
36409  *
36410  * Originally Released Under LGPL - original licence link has changed is not relivant.
36411  *
36412  * Fork - LGPL
36413  * <script type="text/javascript">
36414  */
36415  
36416
36417
36418 /**
36419  * @class Roo.SplitLayoutRegion
36420  * @extends Roo.LayoutRegion
36421  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36422  */
36423 Roo.bootstrap.layout.Split = function(config){
36424     this.cursor = config.cursor;
36425     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36426 };
36427
36428 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36429 {
36430     splitTip : "Drag to resize.",
36431     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36432     useSplitTips : false,
36433
36434     applyConfig : function(config){
36435         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36436     },
36437     
36438     onRender : function(ctr,pos) {
36439         
36440         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36441         if(!this.config.split){
36442             return;
36443         }
36444         if(!this.split){
36445             
36446             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36447                             tag: "div",
36448                             id: this.el.id + "-split",
36449                             cls: "roo-layout-split roo-layout-split-"+this.position,
36450                             html: "&#160;"
36451             });
36452             /** The SplitBar for this region 
36453             * @type Roo.SplitBar */
36454             // does not exist yet...
36455             Roo.log([this.position, this.orientation]);
36456             
36457             this.split = new Roo.bootstrap.SplitBar({
36458                 dragElement : splitEl,
36459                 resizingElement: this.el,
36460                 orientation : this.orientation
36461             });
36462             
36463             this.split.on("moved", this.onSplitMove, this);
36464             this.split.useShim = this.config.useShim === true;
36465             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36466             if(this.useSplitTips){
36467                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36468             }
36469             //if(config.collapsible){
36470             //    this.split.el.on("dblclick", this.collapse,  this);
36471             //}
36472         }
36473         if(typeof this.config.minSize != "undefined"){
36474             this.split.minSize = this.config.minSize;
36475         }
36476         if(typeof this.config.maxSize != "undefined"){
36477             this.split.maxSize = this.config.maxSize;
36478         }
36479         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36480             this.hideSplitter();
36481         }
36482         
36483     },
36484
36485     getHMaxSize : function(){
36486          var cmax = this.config.maxSize || 10000;
36487          var center = this.mgr.getRegion("center");
36488          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36489     },
36490
36491     getVMaxSize : function(){
36492          var cmax = this.config.maxSize || 10000;
36493          var center = this.mgr.getRegion("center");
36494          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36495     },
36496
36497     onSplitMove : function(split, newSize){
36498         this.fireEvent("resized", this, newSize);
36499     },
36500     
36501     /** 
36502      * Returns the {@link Roo.SplitBar} for this region.
36503      * @return {Roo.SplitBar}
36504      */
36505     getSplitBar : function(){
36506         return this.split;
36507     },
36508     
36509     hide : function(){
36510         this.hideSplitter();
36511         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36512     },
36513
36514     hideSplitter : function(){
36515         if(this.split){
36516             this.split.el.setLocation(-2000,-2000);
36517             this.split.el.hide();
36518         }
36519     },
36520
36521     show : function(){
36522         if(this.split){
36523             this.split.el.show();
36524         }
36525         Roo.bootstrap.layout.Split.superclass.show.call(this);
36526     },
36527     
36528     beforeSlide: function(){
36529         if(Roo.isGecko){// firefox overflow auto bug workaround
36530             this.bodyEl.clip();
36531             if(this.tabs) {
36532                 this.tabs.bodyEl.clip();
36533             }
36534             if(this.activePanel){
36535                 this.activePanel.getEl().clip();
36536                 
36537                 if(this.activePanel.beforeSlide){
36538                     this.activePanel.beforeSlide();
36539                 }
36540             }
36541         }
36542     },
36543     
36544     afterSlide : function(){
36545         if(Roo.isGecko){// firefox overflow auto bug workaround
36546             this.bodyEl.unclip();
36547             if(this.tabs) {
36548                 this.tabs.bodyEl.unclip();
36549             }
36550             if(this.activePanel){
36551                 this.activePanel.getEl().unclip();
36552                 if(this.activePanel.afterSlide){
36553                     this.activePanel.afterSlide();
36554                 }
36555             }
36556         }
36557     },
36558
36559     initAutoHide : function(){
36560         if(this.autoHide !== false){
36561             if(!this.autoHideHd){
36562                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36563                 this.autoHideHd = {
36564                     "mouseout": function(e){
36565                         if(!e.within(this.el, true)){
36566                             st.delay(500);
36567                         }
36568                     },
36569                     "mouseover" : function(e){
36570                         st.cancel();
36571                     },
36572                     scope : this
36573                 };
36574             }
36575             this.el.on(this.autoHideHd);
36576         }
36577     },
36578
36579     clearAutoHide : function(){
36580         if(this.autoHide !== false){
36581             this.el.un("mouseout", this.autoHideHd.mouseout);
36582             this.el.un("mouseover", this.autoHideHd.mouseover);
36583         }
36584     },
36585
36586     clearMonitor : function(){
36587         Roo.get(document).un("click", this.slideInIf, this);
36588     },
36589
36590     // these names are backwards but not changed for compat
36591     slideOut : function(){
36592         if(this.isSlid || this.el.hasActiveFx()){
36593             return;
36594         }
36595         this.isSlid = true;
36596         if(this.collapseBtn){
36597             this.collapseBtn.hide();
36598         }
36599         this.closeBtnState = this.closeBtn.getStyle('display');
36600         this.closeBtn.hide();
36601         if(this.stickBtn){
36602             this.stickBtn.show();
36603         }
36604         this.el.show();
36605         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36606         this.beforeSlide();
36607         this.el.setStyle("z-index", 10001);
36608         this.el.slideIn(this.getSlideAnchor(), {
36609             callback: function(){
36610                 this.afterSlide();
36611                 this.initAutoHide();
36612                 Roo.get(document).on("click", this.slideInIf, this);
36613                 this.fireEvent("slideshow", this);
36614             },
36615             scope: this,
36616             block: true
36617         });
36618     },
36619
36620     afterSlideIn : function(){
36621         this.clearAutoHide();
36622         this.isSlid = false;
36623         this.clearMonitor();
36624         this.el.setStyle("z-index", "");
36625         if(this.collapseBtn){
36626             this.collapseBtn.show();
36627         }
36628         this.closeBtn.setStyle('display', this.closeBtnState);
36629         if(this.stickBtn){
36630             this.stickBtn.hide();
36631         }
36632         this.fireEvent("slidehide", this);
36633     },
36634
36635     slideIn : function(cb){
36636         if(!this.isSlid || this.el.hasActiveFx()){
36637             Roo.callback(cb);
36638             return;
36639         }
36640         this.isSlid = false;
36641         this.beforeSlide();
36642         this.el.slideOut(this.getSlideAnchor(), {
36643             callback: function(){
36644                 this.el.setLeftTop(-10000, -10000);
36645                 this.afterSlide();
36646                 this.afterSlideIn();
36647                 Roo.callback(cb);
36648             },
36649             scope: this,
36650             block: true
36651         });
36652     },
36653     
36654     slideInIf : function(e){
36655         if(!e.within(this.el)){
36656             this.slideIn();
36657         }
36658     },
36659
36660     animateCollapse : function(){
36661         this.beforeSlide();
36662         this.el.setStyle("z-index", 20000);
36663         var anchor = this.getSlideAnchor();
36664         this.el.slideOut(anchor, {
36665             callback : function(){
36666                 this.el.setStyle("z-index", "");
36667                 this.collapsedEl.slideIn(anchor, {duration:.3});
36668                 this.afterSlide();
36669                 this.el.setLocation(-10000,-10000);
36670                 this.el.hide();
36671                 this.fireEvent("collapsed", this);
36672             },
36673             scope: this,
36674             block: true
36675         });
36676     },
36677
36678     animateExpand : function(){
36679         this.beforeSlide();
36680         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36681         this.el.setStyle("z-index", 20000);
36682         this.collapsedEl.hide({
36683             duration:.1
36684         });
36685         this.el.slideIn(this.getSlideAnchor(), {
36686             callback : function(){
36687                 this.el.setStyle("z-index", "");
36688                 this.afterSlide();
36689                 if(this.split){
36690                     this.split.el.show();
36691                 }
36692                 this.fireEvent("invalidated", this);
36693                 this.fireEvent("expanded", this);
36694             },
36695             scope: this,
36696             block: true
36697         });
36698     },
36699
36700     anchors : {
36701         "west" : "left",
36702         "east" : "right",
36703         "north" : "top",
36704         "south" : "bottom"
36705     },
36706
36707     sanchors : {
36708         "west" : "l",
36709         "east" : "r",
36710         "north" : "t",
36711         "south" : "b"
36712     },
36713
36714     canchors : {
36715         "west" : "tl-tr",
36716         "east" : "tr-tl",
36717         "north" : "tl-bl",
36718         "south" : "bl-tl"
36719     },
36720
36721     getAnchor : function(){
36722         return this.anchors[this.position];
36723     },
36724
36725     getCollapseAnchor : function(){
36726         return this.canchors[this.position];
36727     },
36728
36729     getSlideAnchor : function(){
36730         return this.sanchors[this.position];
36731     },
36732
36733     getAlignAdj : function(){
36734         var cm = this.cmargins;
36735         switch(this.position){
36736             case "west":
36737                 return [0, 0];
36738             break;
36739             case "east":
36740                 return [0, 0];
36741             break;
36742             case "north":
36743                 return [0, 0];
36744             break;
36745             case "south":
36746                 return [0, 0];
36747             break;
36748         }
36749     },
36750
36751     getExpandAdj : function(){
36752         var c = this.collapsedEl, cm = this.cmargins;
36753         switch(this.position){
36754             case "west":
36755                 return [-(cm.right+c.getWidth()+cm.left), 0];
36756             break;
36757             case "east":
36758                 return [cm.right+c.getWidth()+cm.left, 0];
36759             break;
36760             case "north":
36761                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36762             break;
36763             case "south":
36764                 return [0, cm.top+cm.bottom+c.getHeight()];
36765             break;
36766         }
36767     }
36768 });/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778 /*
36779  * These classes are private internal classes
36780  */
36781 Roo.bootstrap.layout.Center = function(config){
36782     config.region = "center";
36783     Roo.bootstrap.layout.Region.call(this, config);
36784     this.visible = true;
36785     this.minWidth = config.minWidth || 20;
36786     this.minHeight = config.minHeight || 20;
36787 };
36788
36789 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36790     hide : function(){
36791         // center panel can't be hidden
36792     },
36793     
36794     show : function(){
36795         // center panel can't be hidden
36796     },
36797     
36798     getMinWidth: function(){
36799         return this.minWidth;
36800     },
36801     
36802     getMinHeight: function(){
36803         return this.minHeight;
36804     }
36805 });
36806
36807
36808
36809
36810  
36811
36812
36813
36814
36815
36816 Roo.bootstrap.layout.North = function(config)
36817 {
36818     config.region = 'north';
36819     config.cursor = 'n-resize';
36820     
36821     Roo.bootstrap.layout.Split.call(this, config);
36822     
36823     
36824     if(this.split){
36825         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36826         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36827         this.split.el.addClass("roo-layout-split-v");
36828     }
36829     var size = config.initialSize || config.height;
36830     if(typeof size != "undefined"){
36831         this.el.setHeight(size);
36832     }
36833 };
36834 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36835 {
36836     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36837     
36838     
36839     
36840     getBox : function(){
36841         if(this.collapsed){
36842             return this.collapsedEl.getBox();
36843         }
36844         var box = this.el.getBox();
36845         if(this.split){
36846             box.height += this.split.el.getHeight();
36847         }
36848         return box;
36849     },
36850     
36851     updateBox : function(box){
36852         if(this.split && !this.collapsed){
36853             box.height -= this.split.el.getHeight();
36854             this.split.el.setLeft(box.x);
36855             this.split.el.setTop(box.y+box.height);
36856             this.split.el.setWidth(box.width);
36857         }
36858         if(this.collapsed){
36859             this.updateBody(box.width, null);
36860         }
36861         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36862     }
36863 });
36864
36865
36866
36867
36868
36869 Roo.bootstrap.layout.South = function(config){
36870     config.region = 'south';
36871     config.cursor = 's-resize';
36872     Roo.bootstrap.layout.Split.call(this, config);
36873     if(this.split){
36874         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36875         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36876         this.split.el.addClass("roo-layout-split-v");
36877     }
36878     var size = config.initialSize || config.height;
36879     if(typeof size != "undefined"){
36880         this.el.setHeight(size);
36881     }
36882 };
36883
36884 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36885     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36886     getBox : function(){
36887         if(this.collapsed){
36888             return this.collapsedEl.getBox();
36889         }
36890         var box = this.el.getBox();
36891         if(this.split){
36892             var sh = this.split.el.getHeight();
36893             box.height += sh;
36894             box.y -= sh;
36895         }
36896         return box;
36897     },
36898     
36899     updateBox : function(box){
36900         if(this.split && !this.collapsed){
36901             var sh = this.split.el.getHeight();
36902             box.height -= sh;
36903             box.y += sh;
36904             this.split.el.setLeft(box.x);
36905             this.split.el.setTop(box.y-sh);
36906             this.split.el.setWidth(box.width);
36907         }
36908         if(this.collapsed){
36909             this.updateBody(box.width, null);
36910         }
36911         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36912     }
36913 });
36914
36915 Roo.bootstrap.layout.East = function(config){
36916     config.region = "east";
36917     config.cursor = "e-resize";
36918     Roo.bootstrap.layout.Split.call(this, config);
36919     if(this.split){
36920         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36921         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36922         this.split.el.addClass("roo-layout-split-h");
36923     }
36924     var size = config.initialSize || config.width;
36925     if(typeof size != "undefined"){
36926         this.el.setWidth(size);
36927     }
36928 };
36929 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36930     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36931     getBox : function(){
36932         if(this.collapsed){
36933             return this.collapsedEl.getBox();
36934         }
36935         var box = this.el.getBox();
36936         if(this.split){
36937             var sw = this.split.el.getWidth();
36938             box.width += sw;
36939             box.x -= sw;
36940         }
36941         return box;
36942     },
36943
36944     updateBox : function(box){
36945         if(this.split && !this.collapsed){
36946             var sw = this.split.el.getWidth();
36947             box.width -= sw;
36948             this.split.el.setLeft(box.x);
36949             this.split.el.setTop(box.y);
36950             this.split.el.setHeight(box.height);
36951             box.x += sw;
36952         }
36953         if(this.collapsed){
36954             this.updateBody(null, box.height);
36955         }
36956         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36957     }
36958 });
36959
36960 Roo.bootstrap.layout.West = function(config){
36961     config.region = "west";
36962     config.cursor = "w-resize";
36963     
36964     Roo.bootstrap.layout.Split.call(this, config);
36965     if(this.split){
36966         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36967         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36968         this.split.el.addClass("roo-layout-split-h");
36969     }
36970     
36971 };
36972 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36973     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36974     
36975     onRender: function(ctr, pos)
36976     {
36977         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36978         var size = this.config.initialSize || this.config.width;
36979         if(typeof size != "undefined"){
36980             this.el.setWidth(size);
36981         }
36982     },
36983     
36984     getBox : function(){
36985         if(this.collapsed){
36986             return this.collapsedEl.getBox();
36987         }
36988         var box = this.el.getBox();
36989         if(this.split){
36990             box.width += this.split.el.getWidth();
36991         }
36992         return box;
36993     },
36994     
36995     updateBox : function(box){
36996         if(this.split && !this.collapsed){
36997             var sw = this.split.el.getWidth();
36998             box.width -= sw;
36999             this.split.el.setLeft(box.x+box.width);
37000             this.split.el.setTop(box.y);
37001             this.split.el.setHeight(box.height);
37002         }
37003         if(this.collapsed){
37004             this.updateBody(null, box.height);
37005         }
37006         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37007     }
37008 });
37009 Roo.namespace("Roo.bootstrap.panel");/*
37010  * Based on:
37011  * Ext JS Library 1.1.1
37012  * Copyright(c) 2006-2007, Ext JS, LLC.
37013  *
37014  * Originally Released Under LGPL - original licence link has changed is not relivant.
37015  *
37016  * Fork - LGPL
37017  * <script type="text/javascript">
37018  */
37019 /**
37020  * @class Roo.ContentPanel
37021  * @extends Roo.util.Observable
37022  * A basic ContentPanel element.
37023  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37024  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37025  * @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
37026  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37027  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37028  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37029  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37030  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37031  * @cfg {String} title          The title for this panel
37032  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37033  * @cfg {String} url            Calls {@link #setUrl} with this value
37034  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37035  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37036  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37037  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37038  * @cfg {Boolean} badges render the badges
37039
37040  * @constructor
37041  * Create a new ContentPanel.
37042  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37043  * @param {String/Object} config A string to set only the title or a config object
37044  * @param {String} content (optional) Set the HTML content for this panel
37045  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37046  */
37047 Roo.bootstrap.panel.Content = function( config){
37048     
37049     this.tpl = config.tpl || false;
37050     
37051     var el = config.el;
37052     var content = config.content;
37053
37054     if(config.autoCreate){ // xtype is available if this is called from factory
37055         el = Roo.id();
37056     }
37057     this.el = Roo.get(el);
37058     if(!this.el && config && config.autoCreate){
37059         if(typeof config.autoCreate == "object"){
37060             if(!config.autoCreate.id){
37061                 config.autoCreate.id = config.id||el;
37062             }
37063             this.el = Roo.DomHelper.append(document.body,
37064                         config.autoCreate, true);
37065         }else{
37066             var elcfg =  {   tag: "div",
37067                             cls: "roo-layout-inactive-content",
37068                             id: config.id||el
37069                             };
37070             if (config.html) {
37071                 elcfg.html = config.html;
37072                 
37073             }
37074                         
37075             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37076         }
37077     } 
37078     this.closable = false;
37079     this.loaded = false;
37080     this.active = false;
37081    
37082       
37083     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37084         
37085         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37086         
37087         this.wrapEl = this.el; //this.el.wrap();
37088         var ti = [];
37089         if (config.toolbar.items) {
37090             ti = config.toolbar.items ;
37091             delete config.toolbar.items ;
37092         }
37093         
37094         var nitems = [];
37095         this.toolbar.render(this.wrapEl, 'before');
37096         for(var i =0;i < ti.length;i++) {
37097           //  Roo.log(['add child', items[i]]);
37098             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37099         }
37100         this.toolbar.items = nitems;
37101         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37102         delete config.toolbar;
37103         
37104     }
37105     /*
37106     // xtype created footer. - not sure if will work as we normally have to render first..
37107     if (this.footer && !this.footer.el && this.footer.xtype) {
37108         if (!this.wrapEl) {
37109             this.wrapEl = this.el.wrap();
37110         }
37111     
37112         this.footer.container = this.wrapEl.createChild();
37113          
37114         this.footer = Roo.factory(this.footer, Roo);
37115         
37116     }
37117     */
37118     
37119      if(typeof config == "string"){
37120         this.title = config;
37121     }else{
37122         Roo.apply(this, config);
37123     }
37124     
37125     if(this.resizeEl){
37126         this.resizeEl = Roo.get(this.resizeEl, true);
37127     }else{
37128         this.resizeEl = this.el;
37129     }
37130     // handle view.xtype
37131     
37132  
37133     
37134     
37135     this.addEvents({
37136         /**
37137          * @event activate
37138          * Fires when this panel is activated. 
37139          * @param {Roo.ContentPanel} this
37140          */
37141         "activate" : true,
37142         /**
37143          * @event deactivate
37144          * Fires when this panel is activated. 
37145          * @param {Roo.ContentPanel} this
37146          */
37147         "deactivate" : true,
37148
37149         /**
37150          * @event resize
37151          * Fires when this panel is resized if fitToFrame is true.
37152          * @param {Roo.ContentPanel} this
37153          * @param {Number} width The width after any component adjustments
37154          * @param {Number} height The height after any component adjustments
37155          */
37156         "resize" : true,
37157         
37158          /**
37159          * @event render
37160          * Fires when this tab is created
37161          * @param {Roo.ContentPanel} this
37162          */
37163         "render" : true
37164         
37165         
37166         
37167     });
37168     
37169
37170     
37171     
37172     if(this.autoScroll){
37173         this.resizeEl.setStyle("overflow", "auto");
37174     } else {
37175         // fix randome scrolling
37176         //this.el.on('scroll', function() {
37177         //    Roo.log('fix random scolling');
37178         //    this.scrollTo('top',0); 
37179         //});
37180     }
37181     content = content || this.content;
37182     if(content){
37183         this.setContent(content);
37184     }
37185     if(config && config.url){
37186         this.setUrl(this.url, this.params, this.loadOnce);
37187     }
37188     
37189     
37190     
37191     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37192     
37193     if (this.view && typeof(this.view.xtype) != 'undefined') {
37194         this.view.el = this.el.appendChild(document.createElement("div"));
37195         this.view = Roo.factory(this.view); 
37196         this.view.render  &&  this.view.render(false, '');  
37197     }
37198     
37199     
37200     this.fireEvent('render', this);
37201 };
37202
37203 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37204     
37205     tabTip : '',
37206     
37207     setRegion : function(region){
37208         this.region = region;
37209         this.setActiveClass(region && !this.background);
37210     },
37211     
37212     
37213     setActiveClass: function(state)
37214     {
37215         if(state){
37216            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37217            this.el.setStyle('position','relative');
37218         }else{
37219            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37220            this.el.setStyle('position', 'absolute');
37221         } 
37222     },
37223     
37224     /**
37225      * Returns the toolbar for this Panel if one was configured. 
37226      * @return {Roo.Toolbar} 
37227      */
37228     getToolbar : function(){
37229         return this.toolbar;
37230     },
37231     
37232     setActiveState : function(active)
37233     {
37234         this.active = active;
37235         this.setActiveClass(active);
37236         if(!active){
37237             if(this.fireEvent("deactivate", this) === false){
37238                 return false;
37239             }
37240             return true;
37241         }
37242         this.fireEvent("activate", this);
37243         return true;
37244     },
37245     /**
37246      * Updates this panel's element
37247      * @param {String} content The new content
37248      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37249     */
37250     setContent : function(content, loadScripts){
37251         this.el.update(content, loadScripts);
37252     },
37253
37254     ignoreResize : function(w, h){
37255         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37256             return true;
37257         }else{
37258             this.lastSize = {width: w, height: h};
37259             return false;
37260         }
37261     },
37262     /**
37263      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37264      * @return {Roo.UpdateManager} The UpdateManager
37265      */
37266     getUpdateManager : function(){
37267         return this.el.getUpdateManager();
37268     },
37269      /**
37270      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37271      * @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:
37272 <pre><code>
37273 panel.load({
37274     url: "your-url.php",
37275     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37276     callback: yourFunction,
37277     scope: yourObject, //(optional scope)
37278     discardUrl: false,
37279     nocache: false,
37280     text: "Loading...",
37281     timeout: 30,
37282     scripts: false
37283 });
37284 </code></pre>
37285      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37286      * 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.
37287      * @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}
37288      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37289      * @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.
37290      * @return {Roo.ContentPanel} this
37291      */
37292     load : function(){
37293         var um = this.el.getUpdateManager();
37294         um.update.apply(um, arguments);
37295         return this;
37296     },
37297
37298
37299     /**
37300      * 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.
37301      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37302      * @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)
37303      * @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)
37304      * @return {Roo.UpdateManager} The UpdateManager
37305      */
37306     setUrl : function(url, params, loadOnce){
37307         if(this.refreshDelegate){
37308             this.removeListener("activate", this.refreshDelegate);
37309         }
37310         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37311         this.on("activate", this.refreshDelegate);
37312         return this.el.getUpdateManager();
37313     },
37314     
37315     _handleRefresh : function(url, params, loadOnce){
37316         if(!loadOnce || !this.loaded){
37317             var updater = this.el.getUpdateManager();
37318             updater.update(url, params, this._setLoaded.createDelegate(this));
37319         }
37320     },
37321     
37322     _setLoaded : function(){
37323         this.loaded = true;
37324     }, 
37325     
37326     /**
37327      * Returns this panel's id
37328      * @return {String} 
37329      */
37330     getId : function(){
37331         return this.el.id;
37332     },
37333     
37334     /** 
37335      * Returns this panel's element - used by regiosn to add.
37336      * @return {Roo.Element} 
37337      */
37338     getEl : function(){
37339         return this.wrapEl || this.el;
37340     },
37341     
37342    
37343     
37344     adjustForComponents : function(width, height)
37345     {
37346         //Roo.log('adjustForComponents ');
37347         if(this.resizeEl != this.el){
37348             width -= this.el.getFrameWidth('lr');
37349             height -= this.el.getFrameWidth('tb');
37350         }
37351         if(this.toolbar){
37352             var te = this.toolbar.getEl();
37353             te.setWidth(width);
37354             height -= te.getHeight();
37355         }
37356         if(this.footer){
37357             var te = this.footer.getEl();
37358             te.setWidth(width);
37359             height -= te.getHeight();
37360         }
37361         
37362         
37363         if(this.adjustments){
37364             width += this.adjustments[0];
37365             height += this.adjustments[1];
37366         }
37367         return {"width": width, "height": height};
37368     },
37369     
37370     setSize : function(width, height){
37371         if(this.fitToFrame && !this.ignoreResize(width, height)){
37372             if(this.fitContainer && this.resizeEl != this.el){
37373                 this.el.setSize(width, height);
37374             }
37375             var size = this.adjustForComponents(width, height);
37376             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37377             this.fireEvent('resize', this, size.width, size.height);
37378         }
37379     },
37380     
37381     /**
37382      * Returns this panel's title
37383      * @return {String} 
37384      */
37385     getTitle : function(){
37386         
37387         if (typeof(this.title) != 'object') {
37388             return this.title;
37389         }
37390         
37391         var t = '';
37392         for (var k in this.title) {
37393             if (!this.title.hasOwnProperty(k)) {
37394                 continue;
37395             }
37396             
37397             if (k.indexOf('-') >= 0) {
37398                 var s = k.split('-');
37399                 for (var i = 0; i<s.length; i++) {
37400                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37401                 }
37402             } else {
37403                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37404             }
37405         }
37406         return t;
37407     },
37408     
37409     /**
37410      * Set this panel's title
37411      * @param {String} title
37412      */
37413     setTitle : function(title){
37414         this.title = title;
37415         if(this.region){
37416             this.region.updatePanelTitle(this, title);
37417         }
37418     },
37419     
37420     /**
37421      * Returns true is this panel was configured to be closable
37422      * @return {Boolean} 
37423      */
37424     isClosable : function(){
37425         return this.closable;
37426     },
37427     
37428     beforeSlide : function(){
37429         this.el.clip();
37430         this.resizeEl.clip();
37431     },
37432     
37433     afterSlide : function(){
37434         this.el.unclip();
37435         this.resizeEl.unclip();
37436     },
37437     
37438     /**
37439      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37440      *   Will fail silently if the {@link #setUrl} method has not been called.
37441      *   This does not activate the panel, just updates its content.
37442      */
37443     refresh : function(){
37444         if(this.refreshDelegate){
37445            this.loaded = false;
37446            this.refreshDelegate();
37447         }
37448     },
37449     
37450     /**
37451      * Destroys this panel
37452      */
37453     destroy : function(){
37454         this.el.removeAllListeners();
37455         var tempEl = document.createElement("span");
37456         tempEl.appendChild(this.el.dom);
37457         tempEl.innerHTML = "";
37458         this.el.remove();
37459         this.el = null;
37460     },
37461     
37462     /**
37463      * form - if the content panel contains a form - this is a reference to it.
37464      * @type {Roo.form.Form}
37465      */
37466     form : false,
37467     /**
37468      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37469      *    This contains a reference to it.
37470      * @type {Roo.View}
37471      */
37472     view : false,
37473     
37474       /**
37475      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37476      * <pre><code>
37477
37478 layout.addxtype({
37479        xtype : 'Form',
37480        items: [ .... ]
37481    }
37482 );
37483
37484 </code></pre>
37485      * @param {Object} cfg Xtype definition of item to add.
37486      */
37487     
37488     
37489     getChildContainer: function () {
37490         return this.getEl();
37491     }
37492     
37493     
37494     /*
37495         var  ret = new Roo.factory(cfg);
37496         return ret;
37497         
37498         
37499         // add form..
37500         if (cfg.xtype.match(/^Form$/)) {
37501             
37502             var el;
37503             //if (this.footer) {
37504             //    el = this.footer.container.insertSibling(false, 'before');
37505             //} else {
37506                 el = this.el.createChild();
37507             //}
37508
37509             this.form = new  Roo.form.Form(cfg);
37510             
37511             
37512             if ( this.form.allItems.length) {
37513                 this.form.render(el.dom);
37514             }
37515             return this.form;
37516         }
37517         // should only have one of theses..
37518         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37519             // views.. should not be just added - used named prop 'view''
37520             
37521             cfg.el = this.el.appendChild(document.createElement("div"));
37522             // factory?
37523             
37524             var ret = new Roo.factory(cfg);
37525              
37526              ret.render && ret.render(false, ''); // render blank..
37527             this.view = ret;
37528             return ret;
37529         }
37530         return false;
37531     }
37532     \*/
37533 });
37534  
37535 /**
37536  * @class Roo.bootstrap.panel.Grid
37537  * @extends Roo.bootstrap.panel.Content
37538  * @constructor
37539  * Create a new GridPanel.
37540  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37541  * @param {Object} config A the config object
37542   
37543  */
37544
37545
37546
37547 Roo.bootstrap.panel.Grid = function(config)
37548 {
37549     
37550       
37551     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37552         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37553
37554     config.el = this.wrapper;
37555     //this.el = this.wrapper;
37556     
37557       if (config.container) {
37558         // ctor'ed from a Border/panel.grid
37559         
37560         
37561         this.wrapper.setStyle("overflow", "hidden");
37562         this.wrapper.addClass('roo-grid-container');
37563
37564     }
37565     
37566     
37567     if(config.toolbar){
37568         var tool_el = this.wrapper.createChild();    
37569         this.toolbar = Roo.factory(config.toolbar);
37570         var ti = [];
37571         if (config.toolbar.items) {
37572             ti = config.toolbar.items ;
37573             delete config.toolbar.items ;
37574         }
37575         
37576         var nitems = [];
37577         this.toolbar.render(tool_el);
37578         for(var i =0;i < ti.length;i++) {
37579           //  Roo.log(['add child', items[i]]);
37580             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37581         }
37582         this.toolbar.items = nitems;
37583         
37584         delete config.toolbar;
37585     }
37586     
37587     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37588     config.grid.scrollBody = true;;
37589     config.grid.monitorWindowResize = false; // turn off autosizing
37590     config.grid.autoHeight = false;
37591     config.grid.autoWidth = false;
37592     
37593     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37594     
37595     if (config.background) {
37596         // render grid on panel activation (if panel background)
37597         this.on('activate', function(gp) {
37598             if (!gp.grid.rendered) {
37599                 gp.grid.render(this.wrapper);
37600                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37601             }
37602         });
37603             
37604     } else {
37605         this.grid.render(this.wrapper);
37606         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37607
37608     }
37609     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37610     // ??? needed ??? config.el = this.wrapper;
37611     
37612     
37613     
37614   
37615     // xtype created footer. - not sure if will work as we normally have to render first..
37616     if (this.footer && !this.footer.el && this.footer.xtype) {
37617         
37618         var ctr = this.grid.getView().getFooterPanel(true);
37619         this.footer.dataSource = this.grid.dataSource;
37620         this.footer = Roo.factory(this.footer, Roo);
37621         this.footer.render(ctr);
37622         
37623     }
37624     
37625     
37626     
37627     
37628      
37629 };
37630
37631 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37632     getId : function(){
37633         return this.grid.id;
37634     },
37635     
37636     /**
37637      * Returns the grid for this panel
37638      * @return {Roo.bootstrap.Table} 
37639      */
37640     getGrid : function(){
37641         return this.grid;    
37642     },
37643     
37644     setSize : function(width, height){
37645         if(!this.ignoreResize(width, height)){
37646             var grid = this.grid;
37647             var size = this.adjustForComponents(width, height);
37648             var gridel = grid.getGridEl();
37649             gridel.setSize(size.width, size.height);
37650             /*
37651             var thd = grid.getGridEl().select('thead',true).first();
37652             var tbd = grid.getGridEl().select('tbody', true).first();
37653             if (tbd) {
37654                 tbd.setSize(width, height - thd.getHeight());
37655             }
37656             */
37657             grid.autoSize();
37658         }
37659     },
37660      
37661     
37662     
37663     beforeSlide : function(){
37664         this.grid.getView().scroller.clip();
37665     },
37666     
37667     afterSlide : function(){
37668         this.grid.getView().scroller.unclip();
37669     },
37670     
37671     destroy : function(){
37672         this.grid.destroy();
37673         delete this.grid;
37674         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37675     }
37676 });
37677
37678 /**
37679  * @class Roo.bootstrap.panel.Nest
37680  * @extends Roo.bootstrap.panel.Content
37681  * @constructor
37682  * Create a new Panel, that can contain a layout.Border.
37683  * 
37684  * 
37685  * @param {Roo.BorderLayout} layout The layout for this panel
37686  * @param {String/Object} config A string to set only the title or a config object
37687  */
37688 Roo.bootstrap.panel.Nest = function(config)
37689 {
37690     // construct with only one argument..
37691     /* FIXME - implement nicer consturctors
37692     if (layout.layout) {
37693         config = layout;
37694         layout = config.layout;
37695         delete config.layout;
37696     }
37697     if (layout.xtype && !layout.getEl) {
37698         // then layout needs constructing..
37699         layout = Roo.factory(layout, Roo);
37700     }
37701     */
37702     
37703     config.el =  config.layout.getEl();
37704     
37705     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37706     
37707     config.layout.monitorWindowResize = false; // turn off autosizing
37708     this.layout = config.layout;
37709     this.layout.getEl().addClass("roo-layout-nested-layout");
37710     
37711     
37712     
37713     
37714 };
37715
37716 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37717
37718     setSize : function(width, height){
37719         if(!this.ignoreResize(width, height)){
37720             var size = this.adjustForComponents(width, height);
37721             var el = this.layout.getEl();
37722             if (size.height < 1) {
37723                 el.setWidth(size.width);   
37724             } else {
37725                 el.setSize(size.width, size.height);
37726             }
37727             var touch = el.dom.offsetWidth;
37728             this.layout.layout();
37729             // ie requires a double layout on the first pass
37730             if(Roo.isIE && !this.initialized){
37731                 this.initialized = true;
37732                 this.layout.layout();
37733             }
37734         }
37735     },
37736     
37737     // activate all subpanels if not currently active..
37738     
37739     setActiveState : function(active){
37740         this.active = active;
37741         this.setActiveClass(active);
37742         
37743         if(!active){
37744             this.fireEvent("deactivate", this);
37745             return;
37746         }
37747         
37748         this.fireEvent("activate", this);
37749         // not sure if this should happen before or after..
37750         if (!this.layout) {
37751             return; // should not happen..
37752         }
37753         var reg = false;
37754         for (var r in this.layout.regions) {
37755             reg = this.layout.getRegion(r);
37756             if (reg.getActivePanel()) {
37757                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37758                 reg.setActivePanel(reg.getActivePanel());
37759                 continue;
37760             }
37761             if (!reg.panels.length) {
37762                 continue;
37763             }
37764             reg.showPanel(reg.getPanel(0));
37765         }
37766         
37767         
37768         
37769         
37770     },
37771     
37772     /**
37773      * Returns the nested BorderLayout for this panel
37774      * @return {Roo.BorderLayout} 
37775      */
37776     getLayout : function(){
37777         return this.layout;
37778     },
37779     
37780      /**
37781      * Adds a xtype elements to the layout of the nested panel
37782      * <pre><code>
37783
37784 panel.addxtype({
37785        xtype : 'ContentPanel',
37786        region: 'west',
37787        items: [ .... ]
37788    }
37789 );
37790
37791 panel.addxtype({
37792         xtype : 'NestedLayoutPanel',
37793         region: 'west',
37794         layout: {
37795            center: { },
37796            west: { }   
37797         },
37798         items : [ ... list of content panels or nested layout panels.. ]
37799    }
37800 );
37801 </code></pre>
37802      * @param {Object} cfg Xtype definition of item to add.
37803      */
37804     addxtype : function(cfg) {
37805         return this.layout.addxtype(cfg);
37806     
37807     }
37808 });        /*
37809  * Based on:
37810  * Ext JS Library 1.1.1
37811  * Copyright(c) 2006-2007, Ext JS, LLC.
37812  *
37813  * Originally Released Under LGPL - original licence link has changed is not relivant.
37814  *
37815  * Fork - LGPL
37816  * <script type="text/javascript">
37817  */
37818 /**
37819  * @class Roo.TabPanel
37820  * @extends Roo.util.Observable
37821  * A lightweight tab container.
37822  * <br><br>
37823  * Usage:
37824  * <pre><code>
37825 // basic tabs 1, built from existing content
37826 var tabs = new Roo.TabPanel("tabs1");
37827 tabs.addTab("script", "View Script");
37828 tabs.addTab("markup", "View Markup");
37829 tabs.activate("script");
37830
37831 // more advanced tabs, built from javascript
37832 var jtabs = new Roo.TabPanel("jtabs");
37833 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37834
37835 // set up the UpdateManager
37836 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37837 var updater = tab2.getUpdateManager();
37838 updater.setDefaultUrl("ajax1.htm");
37839 tab2.on('activate', updater.refresh, updater, true);
37840
37841 // Use setUrl for Ajax loading
37842 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37843 tab3.setUrl("ajax2.htm", null, true);
37844
37845 // Disabled tab
37846 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37847 tab4.disable();
37848
37849 jtabs.activate("jtabs-1");
37850  * </code></pre>
37851  * @constructor
37852  * Create a new TabPanel.
37853  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37854  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37855  */
37856 Roo.bootstrap.panel.Tabs = function(config){
37857     /**
37858     * The container element for this TabPanel.
37859     * @type Roo.Element
37860     */
37861     this.el = Roo.get(config.el);
37862     delete config.el;
37863     if(config){
37864         if(typeof config == "boolean"){
37865             this.tabPosition = config ? "bottom" : "top";
37866         }else{
37867             Roo.apply(this, config);
37868         }
37869     }
37870     
37871     if(this.tabPosition == "bottom"){
37872         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37873         this.el.addClass("roo-tabs-bottom");
37874     }
37875     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37876     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37877     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37878     if(Roo.isIE){
37879         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37880     }
37881     if(this.tabPosition != "bottom"){
37882         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37883          * @type Roo.Element
37884          */
37885         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37886         this.el.addClass("roo-tabs-top");
37887     }
37888     this.items = [];
37889
37890     this.bodyEl.setStyle("position", "relative");
37891
37892     this.active = null;
37893     this.activateDelegate = this.activate.createDelegate(this);
37894
37895     this.addEvents({
37896         /**
37897          * @event tabchange
37898          * Fires when the active tab changes
37899          * @param {Roo.TabPanel} this
37900          * @param {Roo.TabPanelItem} activePanel The new active tab
37901          */
37902         "tabchange": true,
37903         /**
37904          * @event beforetabchange
37905          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37906          * @param {Roo.TabPanel} this
37907          * @param {Object} e Set cancel to true on this object to cancel the tab change
37908          * @param {Roo.TabPanelItem} tab The tab being changed to
37909          */
37910         "beforetabchange" : true
37911     });
37912
37913     Roo.EventManager.onWindowResize(this.onResize, this);
37914     this.cpad = this.el.getPadding("lr");
37915     this.hiddenCount = 0;
37916
37917
37918     // toolbar on the tabbar support...
37919     if (this.toolbar) {
37920         alert("no toolbar support yet");
37921         this.toolbar  = false;
37922         /*
37923         var tcfg = this.toolbar;
37924         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37925         this.toolbar = new Roo.Toolbar(tcfg);
37926         if (Roo.isSafari) {
37927             var tbl = tcfg.container.child('table', true);
37928             tbl.setAttribute('width', '100%');
37929         }
37930         */
37931         
37932     }
37933    
37934
37935
37936     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37937 };
37938
37939 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37940     /*
37941      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37942      */
37943     tabPosition : "top",
37944     /*
37945      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37946      */
37947     currentTabWidth : 0,
37948     /*
37949      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37950      */
37951     minTabWidth : 40,
37952     /*
37953      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37954      */
37955     maxTabWidth : 250,
37956     /*
37957      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37958      */
37959     preferredTabWidth : 175,
37960     /*
37961      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37962      */
37963     resizeTabs : false,
37964     /*
37965      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37966      */
37967     monitorResize : true,
37968     /*
37969      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37970      */
37971     toolbar : false,
37972
37973     /**
37974      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37975      * @param {String} id The id of the div to use <b>or create</b>
37976      * @param {String} text The text for the tab
37977      * @param {String} content (optional) Content to put in the TabPanelItem body
37978      * @param {Boolean} closable (optional) True to create a close icon on the tab
37979      * @return {Roo.TabPanelItem} The created TabPanelItem
37980      */
37981     addTab : function(id, text, content, closable, tpl)
37982     {
37983         var item = new Roo.bootstrap.panel.TabItem({
37984             panel: this,
37985             id : id,
37986             text : text,
37987             closable : closable,
37988             tpl : tpl
37989         });
37990         this.addTabItem(item);
37991         if(content){
37992             item.setContent(content);
37993         }
37994         return item;
37995     },
37996
37997     /**
37998      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37999      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38000      * @return {Roo.TabPanelItem}
38001      */
38002     getTab : function(id){
38003         return this.items[id];
38004     },
38005
38006     /**
38007      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38008      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38009      */
38010     hideTab : function(id){
38011         var t = this.items[id];
38012         if(!t.isHidden()){
38013            t.setHidden(true);
38014            this.hiddenCount++;
38015            this.autoSizeTabs();
38016         }
38017     },
38018
38019     /**
38020      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38021      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38022      */
38023     unhideTab : function(id){
38024         var t = this.items[id];
38025         if(t.isHidden()){
38026            t.setHidden(false);
38027            this.hiddenCount--;
38028            this.autoSizeTabs();
38029         }
38030     },
38031
38032     /**
38033      * Adds an existing {@link Roo.TabPanelItem}.
38034      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38035      */
38036     addTabItem : function(item){
38037         this.items[item.id] = item;
38038         this.items.push(item);
38039       //  if(this.resizeTabs){
38040     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38041   //         this.autoSizeTabs();
38042 //        }else{
38043 //            item.autoSize();
38044        // }
38045     },
38046
38047     /**
38048      * Removes a {@link Roo.TabPanelItem}.
38049      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38050      */
38051     removeTab : function(id){
38052         var items = this.items;
38053         var tab = items[id];
38054         if(!tab) { return; }
38055         var index = items.indexOf(tab);
38056         if(this.active == tab && items.length > 1){
38057             var newTab = this.getNextAvailable(index);
38058             if(newTab) {
38059                 newTab.activate();
38060             }
38061         }
38062         this.stripEl.dom.removeChild(tab.pnode.dom);
38063         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38064             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38065         }
38066         items.splice(index, 1);
38067         delete this.items[tab.id];
38068         tab.fireEvent("close", tab);
38069         tab.purgeListeners();
38070         this.autoSizeTabs();
38071     },
38072
38073     getNextAvailable : function(start){
38074         var items = this.items;
38075         var index = start;
38076         // look for a next tab that will slide over to
38077         // replace the one being removed
38078         while(index < items.length){
38079             var item = items[++index];
38080             if(item && !item.isHidden()){
38081                 return item;
38082             }
38083         }
38084         // if one isn't found select the previous tab (on the left)
38085         index = start;
38086         while(index >= 0){
38087             var item = items[--index];
38088             if(item && !item.isHidden()){
38089                 return item;
38090             }
38091         }
38092         return null;
38093     },
38094
38095     /**
38096      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38097      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38098      */
38099     disableTab : function(id){
38100         var tab = this.items[id];
38101         if(tab && this.active != tab){
38102             tab.disable();
38103         }
38104     },
38105
38106     /**
38107      * Enables a {@link Roo.TabPanelItem} that is disabled.
38108      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38109      */
38110     enableTab : function(id){
38111         var tab = this.items[id];
38112         tab.enable();
38113     },
38114
38115     /**
38116      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38117      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38118      * @return {Roo.TabPanelItem} The TabPanelItem.
38119      */
38120     activate : function(id){
38121         var tab = this.items[id];
38122         if(!tab){
38123             return null;
38124         }
38125         if(tab == this.active || tab.disabled){
38126             return tab;
38127         }
38128         var e = {};
38129         this.fireEvent("beforetabchange", this, e, tab);
38130         if(e.cancel !== true && !tab.disabled){
38131             if(this.active){
38132                 this.active.hide();
38133             }
38134             this.active = this.items[id];
38135             this.active.show();
38136             this.fireEvent("tabchange", this, this.active);
38137         }
38138         return tab;
38139     },
38140
38141     /**
38142      * Gets the active {@link Roo.TabPanelItem}.
38143      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38144      */
38145     getActiveTab : function(){
38146         return this.active;
38147     },
38148
38149     /**
38150      * Updates the tab body element to fit the height of the container element
38151      * for overflow scrolling
38152      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38153      */
38154     syncHeight : function(targetHeight){
38155         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38156         var bm = this.bodyEl.getMargins();
38157         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38158         this.bodyEl.setHeight(newHeight);
38159         return newHeight;
38160     },
38161
38162     onResize : function(){
38163         if(this.monitorResize){
38164             this.autoSizeTabs();
38165         }
38166     },
38167
38168     /**
38169      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38170      */
38171     beginUpdate : function(){
38172         this.updating = true;
38173     },
38174
38175     /**
38176      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38177      */
38178     endUpdate : function(){
38179         this.updating = false;
38180         this.autoSizeTabs();
38181     },
38182
38183     /**
38184      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38185      */
38186     autoSizeTabs : function(){
38187         var count = this.items.length;
38188         var vcount = count - this.hiddenCount;
38189         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38190             return;
38191         }
38192         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38193         var availWidth = Math.floor(w / vcount);
38194         var b = this.stripBody;
38195         if(b.getWidth() > w){
38196             var tabs = this.items;
38197             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38198             if(availWidth < this.minTabWidth){
38199                 /*if(!this.sleft){    // incomplete scrolling code
38200                     this.createScrollButtons();
38201                 }
38202                 this.showScroll();
38203                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38204             }
38205         }else{
38206             if(this.currentTabWidth < this.preferredTabWidth){
38207                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38208             }
38209         }
38210     },
38211
38212     /**
38213      * Returns the number of tabs in this TabPanel.
38214      * @return {Number}
38215      */
38216      getCount : function(){
38217          return this.items.length;
38218      },
38219
38220     /**
38221      * Resizes all the tabs to the passed width
38222      * @param {Number} The new width
38223      */
38224     setTabWidth : function(width){
38225         this.currentTabWidth = width;
38226         for(var i = 0, len = this.items.length; i < len; i++) {
38227                 if(!this.items[i].isHidden()) {
38228                 this.items[i].setWidth(width);
38229             }
38230         }
38231     },
38232
38233     /**
38234      * Destroys this TabPanel
38235      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38236      */
38237     destroy : function(removeEl){
38238         Roo.EventManager.removeResizeListener(this.onResize, this);
38239         for(var i = 0, len = this.items.length; i < len; i++){
38240             this.items[i].purgeListeners();
38241         }
38242         if(removeEl === true){
38243             this.el.update("");
38244             this.el.remove();
38245         }
38246     },
38247     
38248     createStrip : function(container)
38249     {
38250         var strip = document.createElement("nav");
38251         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38252         container.appendChild(strip);
38253         return strip;
38254     },
38255     
38256     createStripList : function(strip)
38257     {
38258         // div wrapper for retard IE
38259         // returns the "tr" element.
38260         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38261         //'<div class="x-tabs-strip-wrap">'+
38262           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38263           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38264         return strip.firstChild; //.firstChild.firstChild.firstChild;
38265     },
38266     createBody : function(container)
38267     {
38268         var body = document.createElement("div");
38269         Roo.id(body, "tab-body");
38270         //Roo.fly(body).addClass("x-tabs-body");
38271         Roo.fly(body).addClass("tab-content");
38272         container.appendChild(body);
38273         return body;
38274     },
38275     createItemBody :function(bodyEl, id){
38276         var body = Roo.getDom(id);
38277         if(!body){
38278             body = document.createElement("div");
38279             body.id = id;
38280         }
38281         //Roo.fly(body).addClass("x-tabs-item-body");
38282         Roo.fly(body).addClass("tab-pane");
38283          bodyEl.insertBefore(body, bodyEl.firstChild);
38284         return body;
38285     },
38286     /** @private */
38287     createStripElements :  function(stripEl, text, closable, tpl)
38288     {
38289         var td = document.createElement("li"); // was td..
38290         
38291         
38292         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38293         
38294         
38295         stripEl.appendChild(td);
38296         /*if(closable){
38297             td.className = "x-tabs-closable";
38298             if(!this.closeTpl){
38299                 this.closeTpl = new Roo.Template(
38300                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38301                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38302                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38303                 );
38304             }
38305             var el = this.closeTpl.overwrite(td, {"text": text});
38306             var close = el.getElementsByTagName("div")[0];
38307             var inner = el.getElementsByTagName("em")[0];
38308             return {"el": el, "close": close, "inner": inner};
38309         } else {
38310         */
38311         // not sure what this is..
38312 //            if(!this.tabTpl){
38313                 //this.tabTpl = new Roo.Template(
38314                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38315                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38316                 //);
38317 //                this.tabTpl = new Roo.Template(
38318 //                   '<a href="#">' +
38319 //                   '<span unselectable="on"' +
38320 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38321 //                            ' >{text}</span></a>'
38322 //                );
38323 //                
38324 //            }
38325
38326
38327             var template = tpl || this.tabTpl || false;
38328             
38329             if(!template){
38330                 
38331                 template = new Roo.Template(
38332                    '<a href="#">' +
38333                    '<span unselectable="on"' +
38334                             (this.disableTooltips ? '' : ' title="{text}"') +
38335                             ' >{text}</span></a>'
38336                 );
38337             }
38338             
38339             switch (typeof(template)) {
38340                 case 'object' :
38341                     break;
38342                 case 'string' :
38343                     template = new Roo.Template(template);
38344                     break;
38345                 default :
38346                     break;
38347             }
38348             
38349             var el = template.overwrite(td, {"text": text});
38350             
38351             var inner = el.getElementsByTagName("span")[0];
38352             
38353             return {"el": el, "inner": inner};
38354             
38355     }
38356         
38357     
38358 });
38359
38360 /**
38361  * @class Roo.TabPanelItem
38362  * @extends Roo.util.Observable
38363  * Represents an individual item (tab plus body) in a TabPanel.
38364  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38365  * @param {String} id The id of this TabPanelItem
38366  * @param {String} text The text for the tab of this TabPanelItem
38367  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38368  */
38369 Roo.bootstrap.panel.TabItem = function(config){
38370     /**
38371      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38372      * @type Roo.TabPanel
38373      */
38374     this.tabPanel = config.panel;
38375     /**
38376      * The id for this TabPanelItem
38377      * @type String
38378      */
38379     this.id = config.id;
38380     /** @private */
38381     this.disabled = false;
38382     /** @private */
38383     this.text = config.text;
38384     /** @private */
38385     this.loaded = false;
38386     this.closable = config.closable;
38387
38388     /**
38389      * The body element for this TabPanelItem.
38390      * @type Roo.Element
38391      */
38392     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38393     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38394     this.bodyEl.setStyle("display", "block");
38395     this.bodyEl.setStyle("zoom", "1");
38396     //this.hideAction();
38397
38398     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38399     /** @private */
38400     this.el = Roo.get(els.el);
38401     this.inner = Roo.get(els.inner, true);
38402     this.textEl = Roo.get(this.el.dom.firstChild, true);
38403     this.pnode = Roo.get(els.el.parentNode, true);
38404 //    this.el.on("mousedown", this.onTabMouseDown, this);
38405     this.el.on("click", this.onTabClick, this);
38406     /** @private */
38407     if(config.closable){
38408         var c = Roo.get(els.close, true);
38409         c.dom.title = this.closeText;
38410         c.addClassOnOver("close-over");
38411         c.on("click", this.closeClick, this);
38412      }
38413
38414     this.addEvents({
38415          /**
38416          * @event activate
38417          * Fires when this tab becomes the active tab.
38418          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38419          * @param {Roo.TabPanelItem} this
38420          */
38421         "activate": true,
38422         /**
38423          * @event beforeclose
38424          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38425          * @param {Roo.TabPanelItem} this
38426          * @param {Object} e Set cancel to true on this object to cancel the close.
38427          */
38428         "beforeclose": true,
38429         /**
38430          * @event close
38431          * Fires when this tab is closed.
38432          * @param {Roo.TabPanelItem} this
38433          */
38434          "close": true,
38435         /**
38436          * @event deactivate
38437          * Fires when this tab is no longer the active tab.
38438          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38439          * @param {Roo.TabPanelItem} this
38440          */
38441          "deactivate" : true
38442     });
38443     this.hidden = false;
38444
38445     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38446 };
38447
38448 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38449            {
38450     purgeListeners : function(){
38451        Roo.util.Observable.prototype.purgeListeners.call(this);
38452        this.el.removeAllListeners();
38453     },
38454     /**
38455      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38456      */
38457     show : function(){
38458         this.pnode.addClass("active");
38459         this.showAction();
38460         if(Roo.isOpera){
38461             this.tabPanel.stripWrap.repaint();
38462         }
38463         this.fireEvent("activate", this.tabPanel, this);
38464     },
38465
38466     /**
38467      * Returns true if this tab is the active tab.
38468      * @return {Boolean}
38469      */
38470     isActive : function(){
38471         return this.tabPanel.getActiveTab() == this;
38472     },
38473
38474     /**
38475      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38476      */
38477     hide : function(){
38478         this.pnode.removeClass("active");
38479         this.hideAction();
38480         this.fireEvent("deactivate", this.tabPanel, this);
38481     },
38482
38483     hideAction : function(){
38484         this.bodyEl.hide();
38485         this.bodyEl.setStyle("position", "absolute");
38486         this.bodyEl.setLeft("-20000px");
38487         this.bodyEl.setTop("-20000px");
38488     },
38489
38490     showAction : function(){
38491         this.bodyEl.setStyle("position", "relative");
38492         this.bodyEl.setTop("");
38493         this.bodyEl.setLeft("");
38494         this.bodyEl.show();
38495     },
38496
38497     /**
38498      * Set the tooltip for the tab.
38499      * @param {String} tooltip The tab's tooltip
38500      */
38501     setTooltip : function(text){
38502         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38503             this.textEl.dom.qtip = text;
38504             this.textEl.dom.removeAttribute('title');
38505         }else{
38506             this.textEl.dom.title = text;
38507         }
38508     },
38509
38510     onTabClick : function(e){
38511         e.preventDefault();
38512         this.tabPanel.activate(this.id);
38513     },
38514
38515     onTabMouseDown : function(e){
38516         e.preventDefault();
38517         this.tabPanel.activate(this.id);
38518     },
38519 /*
38520     getWidth : function(){
38521         return this.inner.getWidth();
38522     },
38523
38524     setWidth : function(width){
38525         var iwidth = width - this.pnode.getPadding("lr");
38526         this.inner.setWidth(iwidth);
38527         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38528         this.pnode.setWidth(width);
38529     },
38530 */
38531     /**
38532      * Show or hide the tab
38533      * @param {Boolean} hidden True to hide or false to show.
38534      */
38535     setHidden : function(hidden){
38536         this.hidden = hidden;
38537         this.pnode.setStyle("display", hidden ? "none" : "");
38538     },
38539
38540     /**
38541      * Returns true if this tab is "hidden"
38542      * @return {Boolean}
38543      */
38544     isHidden : function(){
38545         return this.hidden;
38546     },
38547
38548     /**
38549      * Returns the text for this tab
38550      * @return {String}
38551      */
38552     getText : function(){
38553         return this.text;
38554     },
38555     /*
38556     autoSize : function(){
38557         //this.el.beginMeasure();
38558         this.textEl.setWidth(1);
38559         /*
38560          *  #2804 [new] Tabs in Roojs
38561          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38562          */
38563         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38564         //this.el.endMeasure();
38565     //},
38566
38567     /**
38568      * Sets the text for the tab (Note: this also sets the tooltip text)
38569      * @param {String} text The tab's text and tooltip
38570      */
38571     setText : function(text){
38572         this.text = text;
38573         this.textEl.update(text);
38574         this.setTooltip(text);
38575         //if(!this.tabPanel.resizeTabs){
38576         //    this.autoSize();
38577         //}
38578     },
38579     /**
38580      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38581      */
38582     activate : function(){
38583         this.tabPanel.activate(this.id);
38584     },
38585
38586     /**
38587      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38588      */
38589     disable : function(){
38590         if(this.tabPanel.active != this){
38591             this.disabled = true;
38592             this.pnode.addClass("disabled");
38593         }
38594     },
38595
38596     /**
38597      * Enables this TabPanelItem if it was previously disabled.
38598      */
38599     enable : function(){
38600         this.disabled = false;
38601         this.pnode.removeClass("disabled");
38602     },
38603
38604     /**
38605      * Sets the content for this TabPanelItem.
38606      * @param {String} content The content
38607      * @param {Boolean} loadScripts true to look for and load scripts
38608      */
38609     setContent : function(content, loadScripts){
38610         this.bodyEl.update(content, loadScripts);
38611     },
38612
38613     /**
38614      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38615      * @return {Roo.UpdateManager} The UpdateManager
38616      */
38617     getUpdateManager : function(){
38618         return this.bodyEl.getUpdateManager();
38619     },
38620
38621     /**
38622      * Set a URL to be used to load the content for this TabPanelItem.
38623      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38624      * @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)
38625      * @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)
38626      * @return {Roo.UpdateManager} The UpdateManager
38627      */
38628     setUrl : function(url, params, loadOnce){
38629         if(this.refreshDelegate){
38630             this.un('activate', this.refreshDelegate);
38631         }
38632         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38633         this.on("activate", this.refreshDelegate);
38634         return this.bodyEl.getUpdateManager();
38635     },
38636
38637     /** @private */
38638     _handleRefresh : function(url, params, loadOnce){
38639         if(!loadOnce || !this.loaded){
38640             var updater = this.bodyEl.getUpdateManager();
38641             updater.update(url, params, this._setLoaded.createDelegate(this));
38642         }
38643     },
38644
38645     /**
38646      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38647      *   Will fail silently if the setUrl method has not been called.
38648      *   This does not activate the panel, just updates its content.
38649      */
38650     refresh : function(){
38651         if(this.refreshDelegate){
38652            this.loaded = false;
38653            this.refreshDelegate();
38654         }
38655     },
38656
38657     /** @private */
38658     _setLoaded : function(){
38659         this.loaded = true;
38660     },
38661
38662     /** @private */
38663     closeClick : function(e){
38664         var o = {};
38665         e.stopEvent();
38666         this.fireEvent("beforeclose", this, o);
38667         if(o.cancel !== true){
38668             this.tabPanel.removeTab(this.id);
38669         }
38670     },
38671     /**
38672      * The text displayed in the tooltip for the close icon.
38673      * @type String
38674      */
38675     closeText : "Close this tab"
38676 });
38677 /**
38678 *    This script refer to:
38679 *    Title: International Telephone Input
38680 *    Author: Jack O'Connor
38681 *    Code version:  v12.1.12
38682 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38683 **/
38684
38685 Roo.bootstrap.PhoneInputData = function() {
38686     var d = [
38687       [
38688         "Afghanistan (‫افغانستان‬‎)",
38689         "af",
38690         "93"
38691       ],
38692       [
38693         "Albania (Shqipëri)",
38694         "al",
38695         "355"
38696       ],
38697       [
38698         "Algeria (‫الجزائر‬‎)",
38699         "dz",
38700         "213"
38701       ],
38702       [
38703         "American Samoa",
38704         "as",
38705         "1684"
38706       ],
38707       [
38708         "Andorra",
38709         "ad",
38710         "376"
38711       ],
38712       [
38713         "Angola",
38714         "ao",
38715         "244"
38716       ],
38717       [
38718         "Anguilla",
38719         "ai",
38720         "1264"
38721       ],
38722       [
38723         "Antigua and Barbuda",
38724         "ag",
38725         "1268"
38726       ],
38727       [
38728         "Argentina",
38729         "ar",
38730         "54"
38731       ],
38732       [
38733         "Armenia (Հայաստան)",
38734         "am",
38735         "374"
38736       ],
38737       [
38738         "Aruba",
38739         "aw",
38740         "297"
38741       ],
38742       [
38743         "Australia",
38744         "au",
38745         "61",
38746         0
38747       ],
38748       [
38749         "Austria (Österreich)",
38750         "at",
38751         "43"
38752       ],
38753       [
38754         "Azerbaijan (Azərbaycan)",
38755         "az",
38756         "994"
38757       ],
38758       [
38759         "Bahamas",
38760         "bs",
38761         "1242"
38762       ],
38763       [
38764         "Bahrain (‫البحرين‬‎)",
38765         "bh",
38766         "973"
38767       ],
38768       [
38769         "Bangladesh (বাংলাদেশ)",
38770         "bd",
38771         "880"
38772       ],
38773       [
38774         "Barbados",
38775         "bb",
38776         "1246"
38777       ],
38778       [
38779         "Belarus (Беларусь)",
38780         "by",
38781         "375"
38782       ],
38783       [
38784         "Belgium (België)",
38785         "be",
38786         "32"
38787       ],
38788       [
38789         "Belize",
38790         "bz",
38791         "501"
38792       ],
38793       [
38794         "Benin (Bénin)",
38795         "bj",
38796         "229"
38797       ],
38798       [
38799         "Bermuda",
38800         "bm",
38801         "1441"
38802       ],
38803       [
38804         "Bhutan (འབྲུག)",
38805         "bt",
38806         "975"
38807       ],
38808       [
38809         "Bolivia",
38810         "bo",
38811         "591"
38812       ],
38813       [
38814         "Bosnia and Herzegovina (Босна и Херцеговина)",
38815         "ba",
38816         "387"
38817       ],
38818       [
38819         "Botswana",
38820         "bw",
38821         "267"
38822       ],
38823       [
38824         "Brazil (Brasil)",
38825         "br",
38826         "55"
38827       ],
38828       [
38829         "British Indian Ocean Territory",
38830         "io",
38831         "246"
38832       ],
38833       [
38834         "British Virgin Islands",
38835         "vg",
38836         "1284"
38837       ],
38838       [
38839         "Brunei",
38840         "bn",
38841         "673"
38842       ],
38843       [
38844         "Bulgaria (България)",
38845         "bg",
38846         "359"
38847       ],
38848       [
38849         "Burkina Faso",
38850         "bf",
38851         "226"
38852       ],
38853       [
38854         "Burundi (Uburundi)",
38855         "bi",
38856         "257"
38857       ],
38858       [
38859         "Cambodia (កម្ពុជា)",
38860         "kh",
38861         "855"
38862       ],
38863       [
38864         "Cameroon (Cameroun)",
38865         "cm",
38866         "237"
38867       ],
38868       [
38869         "Canada",
38870         "ca",
38871         "1",
38872         1,
38873         ["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"]
38874       ],
38875       [
38876         "Cape Verde (Kabu Verdi)",
38877         "cv",
38878         "238"
38879       ],
38880       [
38881         "Caribbean Netherlands",
38882         "bq",
38883         "599",
38884         1
38885       ],
38886       [
38887         "Cayman Islands",
38888         "ky",
38889         "1345"
38890       ],
38891       [
38892         "Central African Republic (République centrafricaine)",
38893         "cf",
38894         "236"
38895       ],
38896       [
38897         "Chad (Tchad)",
38898         "td",
38899         "235"
38900       ],
38901       [
38902         "Chile",
38903         "cl",
38904         "56"
38905       ],
38906       [
38907         "China (中国)",
38908         "cn",
38909         "86"
38910       ],
38911       [
38912         "Christmas Island",
38913         "cx",
38914         "61",
38915         2
38916       ],
38917       [
38918         "Cocos (Keeling) Islands",
38919         "cc",
38920         "61",
38921         1
38922       ],
38923       [
38924         "Colombia",
38925         "co",
38926         "57"
38927       ],
38928       [
38929         "Comoros (‫جزر القمر‬‎)",
38930         "km",
38931         "269"
38932       ],
38933       [
38934         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38935         "cd",
38936         "243"
38937       ],
38938       [
38939         "Congo (Republic) (Congo-Brazzaville)",
38940         "cg",
38941         "242"
38942       ],
38943       [
38944         "Cook Islands",
38945         "ck",
38946         "682"
38947       ],
38948       [
38949         "Costa Rica",
38950         "cr",
38951         "506"
38952       ],
38953       [
38954         "Côte d’Ivoire",
38955         "ci",
38956         "225"
38957       ],
38958       [
38959         "Croatia (Hrvatska)",
38960         "hr",
38961         "385"
38962       ],
38963       [
38964         "Cuba",
38965         "cu",
38966         "53"
38967       ],
38968       [
38969         "Curaçao",
38970         "cw",
38971         "599",
38972         0
38973       ],
38974       [
38975         "Cyprus (Κύπρος)",
38976         "cy",
38977         "357"
38978       ],
38979       [
38980         "Czech Republic (Česká republika)",
38981         "cz",
38982         "420"
38983       ],
38984       [
38985         "Denmark (Danmark)",
38986         "dk",
38987         "45"
38988       ],
38989       [
38990         "Djibouti",
38991         "dj",
38992         "253"
38993       ],
38994       [
38995         "Dominica",
38996         "dm",
38997         "1767"
38998       ],
38999       [
39000         "Dominican Republic (República Dominicana)",
39001         "do",
39002         "1",
39003         2,
39004         ["809", "829", "849"]
39005       ],
39006       [
39007         "Ecuador",
39008         "ec",
39009         "593"
39010       ],
39011       [
39012         "Egypt (‫مصر‬‎)",
39013         "eg",
39014         "20"
39015       ],
39016       [
39017         "El Salvador",
39018         "sv",
39019         "503"
39020       ],
39021       [
39022         "Equatorial Guinea (Guinea Ecuatorial)",
39023         "gq",
39024         "240"
39025       ],
39026       [
39027         "Eritrea",
39028         "er",
39029         "291"
39030       ],
39031       [
39032         "Estonia (Eesti)",
39033         "ee",
39034         "372"
39035       ],
39036       [
39037         "Ethiopia",
39038         "et",
39039         "251"
39040       ],
39041       [
39042         "Falkland Islands (Islas Malvinas)",
39043         "fk",
39044         "500"
39045       ],
39046       [
39047         "Faroe Islands (Føroyar)",
39048         "fo",
39049         "298"
39050       ],
39051       [
39052         "Fiji",
39053         "fj",
39054         "679"
39055       ],
39056       [
39057         "Finland (Suomi)",
39058         "fi",
39059         "358",
39060         0
39061       ],
39062       [
39063         "France",
39064         "fr",
39065         "33"
39066       ],
39067       [
39068         "French Guiana (Guyane française)",
39069         "gf",
39070         "594"
39071       ],
39072       [
39073         "French Polynesia (Polynésie française)",
39074         "pf",
39075         "689"
39076       ],
39077       [
39078         "Gabon",
39079         "ga",
39080         "241"
39081       ],
39082       [
39083         "Gambia",
39084         "gm",
39085         "220"
39086       ],
39087       [
39088         "Georgia (საქართველო)",
39089         "ge",
39090         "995"
39091       ],
39092       [
39093         "Germany (Deutschland)",
39094         "de",
39095         "49"
39096       ],
39097       [
39098         "Ghana (Gaana)",
39099         "gh",
39100         "233"
39101       ],
39102       [
39103         "Gibraltar",
39104         "gi",
39105         "350"
39106       ],
39107       [
39108         "Greece (Ελλάδα)",
39109         "gr",
39110         "30"
39111       ],
39112       [
39113         "Greenland (Kalaallit Nunaat)",
39114         "gl",
39115         "299"
39116       ],
39117       [
39118         "Grenada",
39119         "gd",
39120         "1473"
39121       ],
39122       [
39123         "Guadeloupe",
39124         "gp",
39125         "590",
39126         0
39127       ],
39128       [
39129         "Guam",
39130         "gu",
39131         "1671"
39132       ],
39133       [
39134         "Guatemala",
39135         "gt",
39136         "502"
39137       ],
39138       [
39139         "Guernsey",
39140         "gg",
39141         "44",
39142         1
39143       ],
39144       [
39145         "Guinea (Guinée)",
39146         "gn",
39147         "224"
39148       ],
39149       [
39150         "Guinea-Bissau (Guiné Bissau)",
39151         "gw",
39152         "245"
39153       ],
39154       [
39155         "Guyana",
39156         "gy",
39157         "592"
39158       ],
39159       [
39160         "Haiti",
39161         "ht",
39162         "509"
39163       ],
39164       [
39165         "Honduras",
39166         "hn",
39167         "504"
39168       ],
39169       [
39170         "Hong Kong (香港)",
39171         "hk",
39172         "852"
39173       ],
39174       [
39175         "Hungary (Magyarország)",
39176         "hu",
39177         "36"
39178       ],
39179       [
39180         "Iceland (Ísland)",
39181         "is",
39182         "354"
39183       ],
39184       [
39185         "India (भारत)",
39186         "in",
39187         "91"
39188       ],
39189       [
39190         "Indonesia",
39191         "id",
39192         "62"
39193       ],
39194       [
39195         "Iran (‫ایران‬‎)",
39196         "ir",
39197         "98"
39198       ],
39199       [
39200         "Iraq (‫العراق‬‎)",
39201         "iq",
39202         "964"
39203       ],
39204       [
39205         "Ireland",
39206         "ie",
39207         "353"
39208       ],
39209       [
39210         "Isle of Man",
39211         "im",
39212         "44",
39213         2
39214       ],
39215       [
39216         "Israel (‫ישראל‬‎)",
39217         "il",
39218         "972"
39219       ],
39220       [
39221         "Italy (Italia)",
39222         "it",
39223         "39",
39224         0
39225       ],
39226       [
39227         "Jamaica",
39228         "jm",
39229         "1876"
39230       ],
39231       [
39232         "Japan (日本)",
39233         "jp",
39234         "81"
39235       ],
39236       [
39237         "Jersey",
39238         "je",
39239         "44",
39240         3
39241       ],
39242       [
39243         "Jordan (‫الأردن‬‎)",
39244         "jo",
39245         "962"
39246       ],
39247       [
39248         "Kazakhstan (Казахстан)",
39249         "kz",
39250         "7",
39251         1
39252       ],
39253       [
39254         "Kenya",
39255         "ke",
39256         "254"
39257       ],
39258       [
39259         "Kiribati",
39260         "ki",
39261         "686"
39262       ],
39263       [
39264         "Kosovo",
39265         "xk",
39266         "383"
39267       ],
39268       [
39269         "Kuwait (‫الكويت‬‎)",
39270         "kw",
39271         "965"
39272       ],
39273       [
39274         "Kyrgyzstan (Кыргызстан)",
39275         "kg",
39276         "996"
39277       ],
39278       [
39279         "Laos (ລາວ)",
39280         "la",
39281         "856"
39282       ],
39283       [
39284         "Latvia (Latvija)",
39285         "lv",
39286         "371"
39287       ],
39288       [
39289         "Lebanon (‫لبنان‬‎)",
39290         "lb",
39291         "961"
39292       ],
39293       [
39294         "Lesotho",
39295         "ls",
39296         "266"
39297       ],
39298       [
39299         "Liberia",
39300         "lr",
39301         "231"
39302       ],
39303       [
39304         "Libya (‫ليبيا‬‎)",
39305         "ly",
39306         "218"
39307       ],
39308       [
39309         "Liechtenstein",
39310         "li",
39311         "423"
39312       ],
39313       [
39314         "Lithuania (Lietuva)",
39315         "lt",
39316         "370"
39317       ],
39318       [
39319         "Luxembourg",
39320         "lu",
39321         "352"
39322       ],
39323       [
39324         "Macau (澳門)",
39325         "mo",
39326         "853"
39327       ],
39328       [
39329         "Macedonia (FYROM) (Македонија)",
39330         "mk",
39331         "389"
39332       ],
39333       [
39334         "Madagascar (Madagasikara)",
39335         "mg",
39336         "261"
39337       ],
39338       [
39339         "Malawi",
39340         "mw",
39341         "265"
39342       ],
39343       [
39344         "Malaysia",
39345         "my",
39346         "60"
39347       ],
39348       [
39349         "Maldives",
39350         "mv",
39351         "960"
39352       ],
39353       [
39354         "Mali",
39355         "ml",
39356         "223"
39357       ],
39358       [
39359         "Malta",
39360         "mt",
39361         "356"
39362       ],
39363       [
39364         "Marshall Islands",
39365         "mh",
39366         "692"
39367       ],
39368       [
39369         "Martinique",
39370         "mq",
39371         "596"
39372       ],
39373       [
39374         "Mauritania (‫موريتانيا‬‎)",
39375         "mr",
39376         "222"
39377       ],
39378       [
39379         "Mauritius (Moris)",
39380         "mu",
39381         "230"
39382       ],
39383       [
39384         "Mayotte",
39385         "yt",
39386         "262",
39387         1
39388       ],
39389       [
39390         "Mexico (México)",
39391         "mx",
39392         "52"
39393       ],
39394       [
39395         "Micronesia",
39396         "fm",
39397         "691"
39398       ],
39399       [
39400         "Moldova (Republica Moldova)",
39401         "md",
39402         "373"
39403       ],
39404       [
39405         "Monaco",
39406         "mc",
39407         "377"
39408       ],
39409       [
39410         "Mongolia (Монгол)",
39411         "mn",
39412         "976"
39413       ],
39414       [
39415         "Montenegro (Crna Gora)",
39416         "me",
39417         "382"
39418       ],
39419       [
39420         "Montserrat",
39421         "ms",
39422         "1664"
39423       ],
39424       [
39425         "Morocco (‫المغرب‬‎)",
39426         "ma",
39427         "212",
39428         0
39429       ],
39430       [
39431         "Mozambique (Moçambique)",
39432         "mz",
39433         "258"
39434       ],
39435       [
39436         "Myanmar (Burma) (မြန်မာ)",
39437         "mm",
39438         "95"
39439       ],
39440       [
39441         "Namibia (Namibië)",
39442         "na",
39443         "264"
39444       ],
39445       [
39446         "Nauru",
39447         "nr",
39448         "674"
39449       ],
39450       [
39451         "Nepal (नेपाल)",
39452         "np",
39453         "977"
39454       ],
39455       [
39456         "Netherlands (Nederland)",
39457         "nl",
39458         "31"
39459       ],
39460       [
39461         "New Caledonia (Nouvelle-Calédonie)",
39462         "nc",
39463         "687"
39464       ],
39465       [
39466         "New Zealand",
39467         "nz",
39468         "64"
39469       ],
39470       [
39471         "Nicaragua",
39472         "ni",
39473         "505"
39474       ],
39475       [
39476         "Niger (Nijar)",
39477         "ne",
39478         "227"
39479       ],
39480       [
39481         "Nigeria",
39482         "ng",
39483         "234"
39484       ],
39485       [
39486         "Niue",
39487         "nu",
39488         "683"
39489       ],
39490       [
39491         "Norfolk Island",
39492         "nf",
39493         "672"
39494       ],
39495       [
39496         "North Korea (조선 민주주의 인민 공화국)",
39497         "kp",
39498         "850"
39499       ],
39500       [
39501         "Northern Mariana Islands",
39502         "mp",
39503         "1670"
39504       ],
39505       [
39506         "Norway (Norge)",
39507         "no",
39508         "47",
39509         0
39510       ],
39511       [
39512         "Oman (‫عُمان‬‎)",
39513         "om",
39514         "968"
39515       ],
39516       [
39517         "Pakistan (‫پاکستان‬‎)",
39518         "pk",
39519         "92"
39520       ],
39521       [
39522         "Palau",
39523         "pw",
39524         "680"
39525       ],
39526       [
39527         "Palestine (‫فلسطين‬‎)",
39528         "ps",
39529         "970"
39530       ],
39531       [
39532         "Panama (Panamá)",
39533         "pa",
39534         "507"
39535       ],
39536       [
39537         "Papua New Guinea",
39538         "pg",
39539         "675"
39540       ],
39541       [
39542         "Paraguay",
39543         "py",
39544         "595"
39545       ],
39546       [
39547         "Peru (Perú)",
39548         "pe",
39549         "51"
39550       ],
39551       [
39552         "Philippines",
39553         "ph",
39554         "63"
39555       ],
39556       [
39557         "Poland (Polska)",
39558         "pl",
39559         "48"
39560       ],
39561       [
39562         "Portugal",
39563         "pt",
39564         "351"
39565       ],
39566       [
39567         "Puerto Rico",
39568         "pr",
39569         "1",
39570         3,
39571         ["787", "939"]
39572       ],
39573       [
39574         "Qatar (‫قطر‬‎)",
39575         "qa",
39576         "974"
39577       ],
39578       [
39579         "Réunion (La Réunion)",
39580         "re",
39581         "262",
39582         0
39583       ],
39584       [
39585         "Romania (România)",
39586         "ro",
39587         "40"
39588       ],
39589       [
39590         "Russia (Россия)",
39591         "ru",
39592         "7",
39593         0
39594       ],
39595       [
39596         "Rwanda",
39597         "rw",
39598         "250"
39599       ],
39600       [
39601         "Saint Barthélemy",
39602         "bl",
39603         "590",
39604         1
39605       ],
39606       [
39607         "Saint Helena",
39608         "sh",
39609         "290"
39610       ],
39611       [
39612         "Saint Kitts and Nevis",
39613         "kn",
39614         "1869"
39615       ],
39616       [
39617         "Saint Lucia",
39618         "lc",
39619         "1758"
39620       ],
39621       [
39622         "Saint Martin (Saint-Martin (partie française))",
39623         "mf",
39624         "590",
39625         2
39626       ],
39627       [
39628         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39629         "pm",
39630         "508"
39631       ],
39632       [
39633         "Saint Vincent and the Grenadines",
39634         "vc",
39635         "1784"
39636       ],
39637       [
39638         "Samoa",
39639         "ws",
39640         "685"
39641       ],
39642       [
39643         "San Marino",
39644         "sm",
39645         "378"
39646       ],
39647       [
39648         "São Tomé and Príncipe (São Tomé e Príncipe)",
39649         "st",
39650         "239"
39651       ],
39652       [
39653         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39654         "sa",
39655         "966"
39656       ],
39657       [
39658         "Senegal (Sénégal)",
39659         "sn",
39660         "221"
39661       ],
39662       [
39663         "Serbia (Србија)",
39664         "rs",
39665         "381"
39666       ],
39667       [
39668         "Seychelles",
39669         "sc",
39670         "248"
39671       ],
39672       [
39673         "Sierra Leone",
39674         "sl",
39675         "232"
39676       ],
39677       [
39678         "Singapore",
39679         "sg",
39680         "65"
39681       ],
39682       [
39683         "Sint Maarten",
39684         "sx",
39685         "1721"
39686       ],
39687       [
39688         "Slovakia (Slovensko)",
39689         "sk",
39690         "421"
39691       ],
39692       [
39693         "Slovenia (Slovenija)",
39694         "si",
39695         "386"
39696       ],
39697       [
39698         "Solomon Islands",
39699         "sb",
39700         "677"
39701       ],
39702       [
39703         "Somalia (Soomaaliya)",
39704         "so",
39705         "252"
39706       ],
39707       [
39708         "South Africa",
39709         "za",
39710         "27"
39711       ],
39712       [
39713         "South Korea (대한민국)",
39714         "kr",
39715         "82"
39716       ],
39717       [
39718         "South Sudan (‫جنوب السودان‬‎)",
39719         "ss",
39720         "211"
39721       ],
39722       [
39723         "Spain (España)",
39724         "es",
39725         "34"
39726       ],
39727       [
39728         "Sri Lanka (ශ්‍රී ලංකාව)",
39729         "lk",
39730         "94"
39731       ],
39732       [
39733         "Sudan (‫السودان‬‎)",
39734         "sd",
39735         "249"
39736       ],
39737       [
39738         "Suriname",
39739         "sr",
39740         "597"
39741       ],
39742       [
39743         "Svalbard and Jan Mayen",
39744         "sj",
39745         "47",
39746         1
39747       ],
39748       [
39749         "Swaziland",
39750         "sz",
39751         "268"
39752       ],
39753       [
39754         "Sweden (Sverige)",
39755         "se",
39756         "46"
39757       ],
39758       [
39759         "Switzerland (Schweiz)",
39760         "ch",
39761         "41"
39762       ],
39763       [
39764         "Syria (‫سوريا‬‎)",
39765         "sy",
39766         "963"
39767       ],
39768       [
39769         "Taiwan (台灣)",
39770         "tw",
39771         "886"
39772       ],
39773       [
39774         "Tajikistan",
39775         "tj",
39776         "992"
39777       ],
39778       [
39779         "Tanzania",
39780         "tz",
39781         "255"
39782       ],
39783       [
39784         "Thailand (ไทย)",
39785         "th",
39786         "66"
39787       ],
39788       [
39789         "Timor-Leste",
39790         "tl",
39791         "670"
39792       ],
39793       [
39794         "Togo",
39795         "tg",
39796         "228"
39797       ],
39798       [
39799         "Tokelau",
39800         "tk",
39801         "690"
39802       ],
39803       [
39804         "Tonga",
39805         "to",
39806         "676"
39807       ],
39808       [
39809         "Trinidad and Tobago",
39810         "tt",
39811         "1868"
39812       ],
39813       [
39814         "Tunisia (‫تونس‬‎)",
39815         "tn",
39816         "216"
39817       ],
39818       [
39819         "Turkey (Türkiye)",
39820         "tr",
39821         "90"
39822       ],
39823       [
39824         "Turkmenistan",
39825         "tm",
39826         "993"
39827       ],
39828       [
39829         "Turks and Caicos Islands",
39830         "tc",
39831         "1649"
39832       ],
39833       [
39834         "Tuvalu",
39835         "tv",
39836         "688"
39837       ],
39838       [
39839         "U.S. Virgin Islands",
39840         "vi",
39841         "1340"
39842       ],
39843       [
39844         "Uganda",
39845         "ug",
39846         "256"
39847       ],
39848       [
39849         "Ukraine (Україна)",
39850         "ua",
39851         "380"
39852       ],
39853       [
39854         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39855         "ae",
39856         "971"
39857       ],
39858       [
39859         "United Kingdom",
39860         "gb",
39861         "44",
39862         0
39863       ],
39864       [
39865         "United States",
39866         "us",
39867         "1",
39868         0
39869       ],
39870       [
39871         "Uruguay",
39872         "uy",
39873         "598"
39874       ],
39875       [
39876         "Uzbekistan (Oʻzbekiston)",
39877         "uz",
39878         "998"
39879       ],
39880       [
39881         "Vanuatu",
39882         "vu",
39883         "678"
39884       ],
39885       [
39886         "Vatican City (Città del Vaticano)",
39887         "va",
39888         "39",
39889         1
39890       ],
39891       [
39892         "Venezuela",
39893         "ve",
39894         "58"
39895       ],
39896       [
39897         "Vietnam (Việt Nam)",
39898         "vn",
39899         "84"
39900       ],
39901       [
39902         "Wallis and Futuna (Wallis-et-Futuna)",
39903         "wf",
39904         "681"
39905       ],
39906       [
39907         "Western Sahara (‫الصحراء الغربية‬‎)",
39908         "eh",
39909         "212",
39910         1
39911       ],
39912       [
39913         "Yemen (‫اليمن‬‎)",
39914         "ye",
39915         "967"
39916       ],
39917       [
39918         "Zambia",
39919         "zm",
39920         "260"
39921       ],
39922       [
39923         "Zimbabwe",
39924         "zw",
39925         "263"
39926       ],
39927       [
39928         "Åland Islands",
39929         "ax",
39930         "358",
39931         1
39932       ]
39933   ];
39934   
39935   return d;
39936 }/**
39937 *    This script refer to:
39938 *    Title: International Telephone Input
39939 *    Author: Jack O'Connor
39940 *    Code version:  v12.1.12
39941 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39942 **/
39943
39944 /**
39945  * @class Roo.bootstrap.PhoneInput
39946  * @extends Roo.bootstrap.TriggerField
39947  * An input with International dial-code selection
39948  
39949  * @cfg {String} defaultDialCode default '+852'
39950  * @cfg {Array} preferedCountries default []
39951   
39952  * @constructor
39953  * Create a new PhoneInput.
39954  * @param {Object} config Configuration options
39955  */
39956
39957 Roo.bootstrap.PhoneInput = function(config) {
39958     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39959 };
39960
39961 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39962         
39963         listWidth: undefined,
39964         
39965         selectedClass: 'active',
39966         
39967         invalidClass : "has-warning",
39968         
39969         validClass: 'has-success',
39970         
39971         allowed: '0123456789',
39972         
39973         max_length: 15,
39974         
39975         /**
39976          * @cfg {String} defaultDialCode The default dial code when initializing the input
39977          */
39978         defaultDialCode: '+852',
39979         
39980         /**
39981          * @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
39982          */
39983         preferedCountries: false,
39984         
39985         getAutoCreate : function()
39986         {
39987             var data = Roo.bootstrap.PhoneInputData();
39988             var align = this.labelAlign || this.parentLabelAlign();
39989             var id = Roo.id();
39990             
39991             this.allCountries = [];
39992             this.dialCodeMapping = [];
39993             
39994             for (var i = 0; i < data.length; i++) {
39995               var c = data[i];
39996               this.allCountries[i] = {
39997                 name: c[0],
39998                 iso2: c[1],
39999                 dialCode: c[2],
40000                 priority: c[3] || 0,
40001                 areaCodes: c[4] || null
40002               };
40003               this.dialCodeMapping[c[2]] = {
40004                   name: c[0],
40005                   iso2: c[1],
40006                   priority: c[3] || 0,
40007                   areaCodes: c[4] || null
40008               };
40009             }
40010             
40011             var cfg = {
40012                 cls: 'form-group',
40013                 cn: []
40014             };
40015             
40016             var input =  {
40017                 tag: 'input',
40018                 id : id,
40019                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40020                 maxlength: this.max_length,
40021                 cls : 'form-control tel-input',
40022                 autocomplete: 'new-password'
40023             };
40024             
40025             var hiddenInput = {
40026                 tag: 'input',
40027                 type: 'hidden',
40028                 cls: 'hidden-tel-input'
40029             };
40030             
40031             if (this.name) {
40032                 hiddenInput.name = this.name;
40033             }
40034             
40035             if (this.disabled) {
40036                 input.disabled = true;
40037             }
40038             
40039             var flag_container = {
40040                 tag: 'div',
40041                 cls: 'flag-box',
40042                 cn: [
40043                     {
40044                         tag: 'div',
40045                         cls: 'flag'
40046                     },
40047                     {
40048                         tag: 'div',
40049                         cls: 'caret'
40050                     }
40051                 ]
40052             };
40053             
40054             var box = {
40055                 tag: 'div',
40056                 cls: this.hasFeedback ? 'has-feedback' : '',
40057                 cn: [
40058                     hiddenInput,
40059                     input,
40060                     {
40061                         tag: 'input',
40062                         cls: 'dial-code-holder',
40063                         disabled: true
40064                     }
40065                 ]
40066             };
40067             
40068             var container = {
40069                 cls: 'roo-select2-container input-group',
40070                 cn: [
40071                     flag_container,
40072                     box
40073                 ]
40074             };
40075             
40076             if (this.fieldLabel.length) {
40077                 var indicator = {
40078                     tag: 'i',
40079                     tooltip: 'This field is required'
40080                 };
40081                 
40082                 var label = {
40083                     tag: 'label',
40084                     'for':  id,
40085                     cls: 'control-label',
40086                     cn: []
40087                 };
40088                 
40089                 var label_text = {
40090                     tag: 'span',
40091                     html: this.fieldLabel
40092                 };
40093                 
40094                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40095                 label.cn = [
40096                     indicator,
40097                     label_text
40098                 ];
40099                 
40100                 if(this.indicatorpos == 'right') {
40101                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40102                     label.cn = [
40103                         label_text,
40104                         indicator
40105                     ];
40106                 }
40107                 
40108                 if(align == 'left') {
40109                     container = {
40110                         tag: 'div',
40111                         cn: [
40112                             container
40113                         ]
40114                     };
40115                     
40116                     if(this.labelWidth > 12){
40117                         label.style = "width: " + this.labelWidth + 'px';
40118                     }
40119                     if(this.labelWidth < 13 && this.labelmd == 0){
40120                         this.labelmd = this.labelWidth;
40121                     }
40122                     if(this.labellg > 0){
40123                         label.cls += ' col-lg-' + this.labellg;
40124                         input.cls += ' col-lg-' + (12 - this.labellg);
40125                     }
40126                     if(this.labelmd > 0){
40127                         label.cls += ' col-md-' + this.labelmd;
40128                         container.cls += ' col-md-' + (12 - this.labelmd);
40129                     }
40130                     if(this.labelsm > 0){
40131                         label.cls += ' col-sm-' + this.labelsm;
40132                         container.cls += ' col-sm-' + (12 - this.labelsm);
40133                     }
40134                     if(this.labelxs > 0){
40135                         label.cls += ' col-xs-' + this.labelxs;
40136                         container.cls += ' col-xs-' + (12 - this.labelxs);
40137                     }
40138                 }
40139             }
40140             
40141             cfg.cn = [
40142                 label,
40143                 container
40144             ];
40145             
40146             var settings = this;
40147             
40148             ['xs','sm','md','lg'].map(function(size){
40149                 if (settings[size]) {
40150                     cfg.cls += ' col-' + size + '-' + settings[size];
40151                 }
40152             });
40153             
40154             this.store = new Roo.data.Store({
40155                 proxy : new Roo.data.MemoryProxy({}),
40156                 reader : new Roo.data.JsonReader({
40157                     fields : [
40158                         {
40159                             'name' : 'name',
40160                             'type' : 'string'
40161                         },
40162                         {
40163                             'name' : 'iso2',
40164                             'type' : 'string'
40165                         },
40166                         {
40167                             'name' : 'dialCode',
40168                             'type' : 'string'
40169                         },
40170                         {
40171                             'name' : 'priority',
40172                             'type' : 'string'
40173                         },
40174                         {
40175                             'name' : 'areaCodes',
40176                             'type' : 'string'
40177                         }
40178                     ]
40179                 })
40180             });
40181             
40182             if(!this.preferedCountries) {
40183                 this.preferedCountries = [
40184                     'hk',
40185                     'gb',
40186                     'us'
40187                 ];
40188             }
40189             
40190             var p = this.preferedCountries.reverse();
40191             
40192             if(p) {
40193                 for (var i = 0; i < p.length; i++) {
40194                     for (var j = 0; j < this.allCountries.length; j++) {
40195                         if(this.allCountries[j].iso2 == p[i]) {
40196                             var t = this.allCountries[j];
40197                             this.allCountries.splice(j,1);
40198                             this.allCountries.unshift(t);
40199                         }
40200                     } 
40201                 }
40202             }
40203             
40204             this.store.proxy.data = {
40205                 success: true,
40206                 data: this.allCountries
40207             };
40208             
40209             return cfg;
40210         },
40211         
40212         initEvents : function()
40213         {
40214             this.createList();
40215             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40216             
40217             this.indicator = this.indicatorEl();
40218             this.flag = this.flagEl();
40219             this.dialCodeHolder = this.dialCodeHolderEl();
40220             
40221             this.trigger = this.el.select('div.flag-box',true).first();
40222             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40223             
40224             var _this = this;
40225             
40226             (function(){
40227                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40228                 _this.list.setWidth(lw);
40229             }).defer(100);
40230             
40231             this.list.on('mouseover', this.onViewOver, this);
40232             this.list.on('mousemove', this.onViewMove, this);
40233             this.inputEl().on("keyup", this.onKeyUp, this);
40234             this.inputEl().on("keypress", this.onKeyPress, this);
40235             
40236             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40237
40238             this.view = new Roo.View(this.list, this.tpl, {
40239                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40240             });
40241             
40242             this.view.on('click', this.onViewClick, this);
40243             this.setValue(this.defaultDialCode);
40244         },
40245         
40246         onTriggerClick : function(e)
40247         {
40248             Roo.log('trigger click');
40249             if(this.disabled){
40250                 return;
40251             }
40252             
40253             if(this.isExpanded()){
40254                 this.collapse();
40255                 this.hasFocus = false;
40256             }else {
40257                 this.store.load({});
40258                 this.hasFocus = true;
40259                 this.expand();
40260             }
40261         },
40262         
40263         isExpanded : function()
40264         {
40265             return this.list.isVisible();
40266         },
40267         
40268         collapse : function()
40269         {
40270             if(!this.isExpanded()){
40271                 return;
40272             }
40273             this.list.hide();
40274             Roo.get(document).un('mousedown', this.collapseIf, this);
40275             Roo.get(document).un('mousewheel', this.collapseIf, this);
40276             this.fireEvent('collapse', this);
40277             this.validate();
40278         },
40279         
40280         expand : function()
40281         {
40282             Roo.log('expand');
40283
40284             if(this.isExpanded() || !this.hasFocus){
40285                 return;
40286             }
40287             
40288             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40289             this.list.setWidth(lw);
40290             
40291             this.list.show();
40292             this.restrictHeight();
40293             
40294             Roo.get(document).on('mousedown', this.collapseIf, this);
40295             Roo.get(document).on('mousewheel', this.collapseIf, this);
40296             
40297             this.fireEvent('expand', this);
40298         },
40299         
40300         restrictHeight : function()
40301         {
40302             this.list.alignTo(this.inputEl(), this.listAlign);
40303             this.list.alignTo(this.inputEl(), this.listAlign);
40304         },
40305         
40306         onViewOver : function(e, t)
40307         {
40308             if(this.inKeyMode){
40309                 return;
40310             }
40311             var item = this.view.findItemFromChild(t);
40312             
40313             if(item){
40314                 var index = this.view.indexOf(item);
40315                 this.select(index, false);
40316             }
40317         },
40318
40319         // private
40320         onViewClick : function(view, doFocus, el, e)
40321         {
40322             var index = this.view.getSelectedIndexes()[0];
40323             
40324             var r = this.store.getAt(index);
40325             
40326             if(r){
40327                 this.onSelect(r, index);
40328             }
40329             if(doFocus !== false && !this.blockFocus){
40330                 this.inputEl().focus();
40331             }
40332         },
40333         
40334         onViewMove : function(e, t)
40335         {
40336             this.inKeyMode = false;
40337         },
40338         
40339         select : function(index, scrollIntoView)
40340         {
40341             this.selectedIndex = index;
40342             this.view.select(index);
40343             if(scrollIntoView !== false){
40344                 var el = this.view.getNode(index);
40345                 if(el){
40346                     this.list.scrollChildIntoView(el, false);
40347                 }
40348             }
40349         },
40350         
40351         createList : function()
40352         {
40353             this.list = Roo.get(document.body).createChild({
40354                 tag: 'ul',
40355                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40356                 style: 'display:none'
40357             });
40358             
40359             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40360         },
40361         
40362         collapseIf : function(e)
40363         {
40364             var in_combo  = e.within(this.el);
40365             var in_list =  e.within(this.list);
40366             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40367             
40368             if (in_combo || in_list || is_list) {
40369                 return;
40370             }
40371             this.collapse();
40372         },
40373         
40374         onSelect : function(record, index)
40375         {
40376             if(this.fireEvent('beforeselect', this, record, index) !== false){
40377                 
40378                 this.setFlagClass(record.data.iso2);
40379                 this.setDialCode(record.data.dialCode);
40380                 this.hasFocus = false;
40381                 this.collapse();
40382                 this.fireEvent('select', this, record, index);
40383             }
40384         },
40385         
40386         flagEl : function()
40387         {
40388             var flag = this.el.select('div.flag',true).first();
40389             if(!flag){
40390                 return false;
40391             }
40392             return flag;
40393         },
40394         
40395         dialCodeHolderEl : function()
40396         {
40397             var d = this.el.select('input.dial-code-holder',true).first();
40398             if(!d){
40399                 return false;
40400             }
40401             return d;
40402         },
40403         
40404         setDialCode : function(v)
40405         {
40406             this.dialCodeHolder.dom.value = '+'+v;
40407         },
40408         
40409         setFlagClass : function(n)
40410         {
40411             this.flag.dom.className = 'flag '+n;
40412         },
40413         
40414         getValue : function()
40415         {
40416             var v = this.inputEl().getValue();
40417             if(this.dialCodeHolder) {
40418                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40419             }
40420             return v;
40421         },
40422         
40423         setValue : function(v)
40424         {
40425             var d = this.getDialCode(v);
40426             
40427             //invalid dial code
40428             if(v.length == 0 || !d || d.length == 0) {
40429                 if(this.rendered){
40430                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40431                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40432                 }
40433                 return;
40434             }
40435             
40436             //valid dial code
40437             this.setFlagClass(this.dialCodeMapping[d].iso2);
40438             this.setDialCode(d);
40439             this.inputEl().dom.value = v.replace('+'+d,'');
40440             this.hiddenEl().dom.value = this.getValue();
40441             
40442             this.validate();
40443         },
40444         
40445         getDialCode : function(v)
40446         {
40447             v = v ||  '';
40448             
40449             if (v.length == 0) {
40450                 return this.dialCodeHolder.dom.value;
40451             }
40452             
40453             var dialCode = "";
40454             if (v.charAt(0) != "+") {
40455                 return false;
40456             }
40457             var numericChars = "";
40458             for (var i = 1; i < v.length; i++) {
40459               var c = v.charAt(i);
40460               if (!isNaN(c)) {
40461                 numericChars += c;
40462                 if (this.dialCodeMapping[numericChars]) {
40463                   dialCode = v.substr(1, i);
40464                 }
40465                 if (numericChars.length == 4) {
40466                   break;
40467                 }
40468               }
40469             }
40470             return dialCode;
40471         },
40472         
40473         reset : function()
40474         {
40475             this.setValue(this.defaultDialCode);
40476             this.validate();
40477         },
40478         
40479         hiddenEl : function()
40480         {
40481             return this.el.select('input.hidden-tel-input',true).first();
40482         },
40483         
40484         // after setting val
40485         onKeyUp : function(e){
40486             this.setValue(this.getValue());
40487         },
40488         
40489         onKeyPress : function(e){
40490             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40491                 e.stopEvent();
40492             }
40493         }
40494         
40495 });
40496 /**
40497  * @class Roo.bootstrap.MoneyField
40498  * @extends Roo.bootstrap.ComboBox
40499  * Bootstrap MoneyField class
40500  * 
40501  * @constructor
40502  * Create a new MoneyField.
40503  * @param {Object} config Configuration options
40504  */
40505
40506 Roo.bootstrap.MoneyField = function(config) {
40507     
40508     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40509     
40510 };
40511
40512 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40513     
40514     /**
40515      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40516      */
40517     allowDecimals : true,
40518     /**
40519      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40520      */
40521     decimalSeparator : ".",
40522     /**
40523      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40524      */
40525     decimalPrecision : 0,
40526     /**
40527      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40528      */
40529     allowNegative : true,
40530     /**
40531      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40532      */
40533     allowZero: true,
40534     /**
40535      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40536      */
40537     minValue : Number.NEGATIVE_INFINITY,
40538     /**
40539      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40540      */
40541     maxValue : Number.MAX_VALUE,
40542     /**
40543      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40544      */
40545     minText : "The minimum value for this field is {0}",
40546     /**
40547      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40548      */
40549     maxText : "The maximum value for this field is {0}",
40550     /**
40551      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40552      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40553      */
40554     nanText : "{0} is not a valid number",
40555     /**
40556      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40557      */
40558     castInt : true,
40559     /**
40560      * @cfg {String} defaults currency of the MoneyField
40561      * value should be in lkey
40562      */
40563     defaultCurrency : false,
40564     /**
40565      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40566      */
40567     thousandsDelimiter : false,
40568     /**
40569      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40570      */
40571     max_length: false,
40572     
40573     inputlg : 9,
40574     inputmd : 9,
40575     inputsm : 9,
40576     inputxs : 6,
40577     
40578     store : false,
40579     
40580     getAutoCreate : function()
40581     {
40582         var align = this.labelAlign || this.parentLabelAlign();
40583         
40584         var id = Roo.id();
40585
40586         var cfg = {
40587             cls: 'form-group',
40588             cn: []
40589         };
40590
40591         var input =  {
40592             tag: 'input',
40593             id : id,
40594             cls : 'form-control roo-money-amount-input',
40595             autocomplete: 'new-password'
40596         };
40597         
40598         var hiddenInput = {
40599             tag: 'input',
40600             type: 'hidden',
40601             id: Roo.id(),
40602             cls: 'hidden-number-input'
40603         };
40604         
40605         if(this.max_length) {
40606             input.maxlength = this.max_length; 
40607         }
40608         
40609         if (this.name) {
40610             hiddenInput.name = this.name;
40611         }
40612
40613         if (this.disabled) {
40614             input.disabled = true;
40615         }
40616
40617         var clg = 12 - this.inputlg;
40618         var cmd = 12 - this.inputmd;
40619         var csm = 12 - this.inputsm;
40620         var cxs = 12 - this.inputxs;
40621         
40622         var container = {
40623             tag : 'div',
40624             cls : 'row roo-money-field',
40625             cn : [
40626                 {
40627                     tag : 'div',
40628                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40629                     cn : [
40630                         {
40631                             tag : 'div',
40632                             cls: 'roo-select2-container input-group',
40633                             cn: [
40634                                 {
40635                                     tag : 'input',
40636                                     cls : 'form-control roo-money-currency-input',
40637                                     autocomplete: 'new-password',
40638                                     readOnly : 1,
40639                                     name : this.currencyName
40640                                 },
40641                                 {
40642                                     tag :'span',
40643                                     cls : 'input-group-addon',
40644                                     cn : [
40645                                         {
40646                                             tag: 'span',
40647                                             cls: 'caret'
40648                                         }
40649                                     ]
40650                                 }
40651                             ]
40652                         }
40653                     ]
40654                 },
40655                 {
40656                     tag : 'div',
40657                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40658                     cn : [
40659                         {
40660                             tag: 'div',
40661                             cls: this.hasFeedback ? 'has-feedback' : '',
40662                             cn: [
40663                                 input
40664                             ]
40665                         }
40666                     ]
40667                 }
40668             ]
40669             
40670         };
40671         
40672         if (this.fieldLabel.length) {
40673             var indicator = {
40674                 tag: 'i',
40675                 tooltip: 'This field is required'
40676             };
40677
40678             var label = {
40679                 tag: 'label',
40680                 'for':  id,
40681                 cls: 'control-label',
40682                 cn: []
40683             };
40684
40685             var label_text = {
40686                 tag: 'span',
40687                 html: this.fieldLabel
40688             };
40689
40690             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40691             label.cn = [
40692                 indicator,
40693                 label_text
40694             ];
40695
40696             if(this.indicatorpos == 'right') {
40697                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40698                 label.cn = [
40699                     label_text,
40700                     indicator
40701                 ];
40702             }
40703
40704             if(align == 'left') {
40705                 container = {
40706                     tag: 'div',
40707                     cn: [
40708                         container
40709                     ]
40710                 };
40711
40712                 if(this.labelWidth > 12){
40713                     label.style = "width: " + this.labelWidth + 'px';
40714                 }
40715                 if(this.labelWidth < 13 && this.labelmd == 0){
40716                     this.labelmd = this.labelWidth;
40717                 }
40718                 if(this.labellg > 0){
40719                     label.cls += ' col-lg-' + this.labellg;
40720                     input.cls += ' col-lg-' + (12 - this.labellg);
40721                 }
40722                 if(this.labelmd > 0){
40723                     label.cls += ' col-md-' + this.labelmd;
40724                     container.cls += ' col-md-' + (12 - this.labelmd);
40725                 }
40726                 if(this.labelsm > 0){
40727                     label.cls += ' col-sm-' + this.labelsm;
40728                     container.cls += ' col-sm-' + (12 - this.labelsm);
40729                 }
40730                 if(this.labelxs > 0){
40731                     label.cls += ' col-xs-' + this.labelxs;
40732                     container.cls += ' col-xs-' + (12 - this.labelxs);
40733                 }
40734             }
40735         }
40736
40737         cfg.cn = [
40738             label,
40739             container,
40740             hiddenInput
40741         ];
40742         
40743         var settings = this;
40744
40745         ['xs','sm','md','lg'].map(function(size){
40746             if (settings[size]) {
40747                 cfg.cls += ' col-' + size + '-' + settings[size];
40748             }
40749         });
40750         
40751         return cfg;
40752     },
40753     
40754     initEvents : function()
40755     {
40756         this.indicator = this.indicatorEl();
40757         
40758         this.initCurrencyEvent();
40759         
40760         this.initNumberEvent();
40761     },
40762     
40763     initCurrencyEvent : function()
40764     {
40765         if (!this.store) {
40766             throw "can not find store for combo";
40767         }
40768         
40769         this.store = Roo.factory(this.store, Roo.data);
40770         this.store.parent = this;
40771         
40772         this.createList();
40773         
40774         this.triggerEl = this.el.select('.input-group-addon', true).first();
40775         
40776         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40777         
40778         var _this = this;
40779         
40780         (function(){
40781             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40782             _this.list.setWidth(lw);
40783         }).defer(100);
40784         
40785         this.list.on('mouseover', this.onViewOver, this);
40786         this.list.on('mousemove', this.onViewMove, this);
40787         this.list.on('scroll', this.onViewScroll, this);
40788         
40789         if(!this.tpl){
40790             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40791         }
40792         
40793         this.view = new Roo.View(this.list, this.tpl, {
40794             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40795         });
40796         
40797         this.view.on('click', this.onViewClick, this);
40798         
40799         this.store.on('beforeload', this.onBeforeLoad, this);
40800         this.store.on('load', this.onLoad, this);
40801         this.store.on('loadexception', this.onLoadException, this);
40802         
40803         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40804             "up" : function(e){
40805                 this.inKeyMode = true;
40806                 this.selectPrev();
40807             },
40808
40809             "down" : function(e){
40810                 if(!this.isExpanded()){
40811                     this.onTriggerClick();
40812                 }else{
40813                     this.inKeyMode = true;
40814                     this.selectNext();
40815                 }
40816             },
40817
40818             "enter" : function(e){
40819                 this.collapse();
40820                 
40821                 if(this.fireEvent("specialkey", this, e)){
40822                     this.onViewClick(false);
40823                 }
40824                 
40825                 return true;
40826             },
40827
40828             "esc" : function(e){
40829                 this.collapse();
40830             },
40831
40832             "tab" : function(e){
40833                 this.collapse();
40834                 
40835                 if(this.fireEvent("specialkey", this, e)){
40836                     this.onViewClick(false);
40837                 }
40838                 
40839                 return true;
40840             },
40841
40842             scope : this,
40843
40844             doRelay : function(foo, bar, hname){
40845                 if(hname == 'down' || this.scope.isExpanded()){
40846                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40847                 }
40848                 return true;
40849             },
40850
40851             forceKeyDown: true
40852         });
40853         
40854         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40855         
40856     },
40857     
40858     initNumberEvent : function(e)
40859     {
40860         this.inputEl().on("keydown" , this.fireKey,  this);
40861         this.inputEl().on("focus", this.onFocus,  this);
40862         this.inputEl().on("blur", this.onBlur,  this);
40863         
40864         this.inputEl().relayEvent('keyup', this);
40865         
40866         if(this.indicator){
40867             this.indicator.addClass('invisible');
40868         }
40869  
40870         this.originalValue = this.getValue();
40871         
40872         if(this.validationEvent == 'keyup'){
40873             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40874             this.inputEl().on('keyup', this.filterValidation, this);
40875         }
40876         else if(this.validationEvent !== false){
40877             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40878         }
40879         
40880         if(this.selectOnFocus){
40881             this.on("focus", this.preFocus, this);
40882             
40883         }
40884         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40885             this.inputEl().on("keypress", this.filterKeys, this);
40886         } else {
40887             this.inputEl().relayEvent('keypress', this);
40888         }
40889         
40890         var allowed = "0123456789";
40891         
40892         if(this.allowDecimals){
40893             allowed += this.decimalSeparator;
40894         }
40895         
40896         if(this.allowNegative){
40897             allowed += "-";
40898         }
40899         
40900         if(this.thousandsDelimiter) {
40901             allowed += ",";
40902         }
40903         
40904         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40905         
40906         var keyPress = function(e){
40907             
40908             var k = e.getKey();
40909             
40910             var c = e.getCharCode();
40911             
40912             if(
40913                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40914                     allowed.indexOf(String.fromCharCode(c)) === -1
40915             ){
40916                 e.stopEvent();
40917                 return;
40918             }
40919             
40920             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40921                 return;
40922             }
40923             
40924             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40925                 e.stopEvent();
40926             }
40927         };
40928         
40929         this.inputEl().on("keypress", keyPress, this);
40930         
40931     },
40932     
40933     onTriggerClick : function(e)
40934     {   
40935         if(this.disabled){
40936             return;
40937         }
40938         
40939         this.page = 0;
40940         this.loadNext = false;
40941         
40942         if(this.isExpanded()){
40943             this.collapse();
40944             return;
40945         }
40946         
40947         this.hasFocus = true;
40948         
40949         if(this.triggerAction == 'all') {
40950             this.doQuery(this.allQuery, true);
40951             return;
40952         }
40953         
40954         this.doQuery(this.getRawValue());
40955     },
40956     
40957     getCurrency : function()
40958     {   
40959         var v = this.currencyEl().getValue();
40960         
40961         return v;
40962     },
40963     
40964     restrictHeight : function()
40965     {
40966         this.list.alignTo(this.currencyEl(), this.listAlign);
40967         this.list.alignTo(this.currencyEl(), this.listAlign);
40968     },
40969     
40970     onViewClick : function(view, doFocus, el, e)
40971     {
40972         var index = this.view.getSelectedIndexes()[0];
40973         
40974         var r = this.store.getAt(index);
40975         
40976         if(r){
40977             this.onSelect(r, index);
40978         }
40979     },
40980     
40981     onSelect : function(record, index){
40982         
40983         if(this.fireEvent('beforeselect', this, record, index) !== false){
40984         
40985             this.setFromCurrencyData(index > -1 ? record.data : false);
40986             
40987             this.collapse();
40988             
40989             this.fireEvent('select', this, record, index);
40990         }
40991     },
40992     
40993     setFromCurrencyData : function(o)
40994     {
40995         var currency = '';
40996         
40997         this.lastCurrency = o;
40998         
40999         if (this.currencyField) {
41000             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41001         } else {
41002             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41003         }
41004         
41005         this.lastSelectionText = currency;
41006         
41007         //setting default currency
41008         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41009             this.setCurrency(this.defaultCurrency);
41010             return;
41011         }
41012         
41013         this.setCurrency(currency);
41014     },
41015     
41016     setFromData : function(o)
41017     {
41018         var c = {};
41019         
41020         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41021         
41022         this.setFromCurrencyData(c);
41023         
41024         var value = '';
41025         
41026         if (this.name) {
41027             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41028         } else {
41029             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41030         }
41031         
41032         this.setValue(value);
41033         
41034     },
41035     
41036     setCurrency : function(v)
41037     {   
41038         this.currencyValue = v;
41039         
41040         if(this.rendered){
41041             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41042             this.validate();
41043         }
41044     },
41045     
41046     setValue : function(v)
41047     {
41048         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41049         
41050         this.value = v;
41051         
41052         if(this.rendered){
41053             
41054             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41055             
41056             this.inputEl().dom.value = (v == '') ? '' :
41057                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41058             
41059             if(!this.allowZero && v === '0') {
41060                 this.hiddenEl().dom.value = '';
41061                 this.inputEl().dom.value = '';
41062             }
41063             
41064             this.validate();
41065         }
41066     },
41067     
41068     getRawValue : function()
41069     {
41070         var v = this.inputEl().getValue();
41071         
41072         return v;
41073     },
41074     
41075     getValue : function()
41076     {
41077         return this.fixPrecision(this.parseValue(this.getRawValue()));
41078     },
41079     
41080     parseValue : function(value)
41081     {
41082         if(this.thousandsDelimiter) {
41083             value += "";
41084             r = new RegExp(",", "g");
41085             value = value.replace(r, "");
41086         }
41087         
41088         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41089         return isNaN(value) ? '' : value;
41090         
41091     },
41092     
41093     fixPrecision : function(value)
41094     {
41095         if(this.thousandsDelimiter) {
41096             value += "";
41097             r = new RegExp(",", "g");
41098             value = value.replace(r, "");
41099         }
41100         
41101         var nan = isNaN(value);
41102         
41103         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41104             return nan ? '' : value;
41105         }
41106         return parseFloat(value).toFixed(this.decimalPrecision);
41107     },
41108     
41109     decimalPrecisionFcn : function(v)
41110     {
41111         return Math.floor(v);
41112     },
41113     
41114     validateValue : function(value)
41115     {
41116         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41117             return false;
41118         }
41119         
41120         var num = this.parseValue(value);
41121         
41122         if(isNaN(num)){
41123             this.markInvalid(String.format(this.nanText, value));
41124             return false;
41125         }
41126         
41127         if(num < this.minValue){
41128             this.markInvalid(String.format(this.minText, this.minValue));
41129             return false;
41130         }
41131         
41132         if(num > this.maxValue){
41133             this.markInvalid(String.format(this.maxText, this.maxValue));
41134             return false;
41135         }
41136         
41137         return true;
41138     },
41139     
41140     validate : function()
41141     {
41142         if(this.disabled || this.allowBlank){
41143             this.markValid();
41144             return true;
41145         }
41146         
41147         var currency = this.getCurrency();
41148         
41149         if(this.validateValue(this.getRawValue()) && currency.length){
41150             this.markValid();
41151             return true;
41152         }
41153         
41154         this.markInvalid();
41155         return false;
41156     },
41157     
41158     getName: function()
41159     {
41160         return this.name;
41161     },
41162     
41163     beforeBlur : function()
41164     {
41165         if(!this.castInt){
41166             return;
41167         }
41168         
41169         var v = this.parseValue(this.getRawValue());
41170         
41171         if(v || v == 0){
41172             this.setValue(v);
41173         }
41174     },
41175     
41176     onBlur : function()
41177     {
41178         this.beforeBlur();
41179         
41180         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41181             //this.el.removeClass(this.focusClass);
41182         }
41183         
41184         this.hasFocus = false;
41185         
41186         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41187             this.validate();
41188         }
41189         
41190         var v = this.getValue();
41191         
41192         if(String(v) !== String(this.startValue)){
41193             this.fireEvent('change', this, v, this.startValue);
41194         }
41195         
41196         this.fireEvent("blur", this);
41197     },
41198     
41199     inputEl : function()
41200     {
41201         return this.el.select('.roo-money-amount-input', true).first();
41202     },
41203     
41204     currencyEl : function()
41205     {
41206         return this.el.select('.roo-money-currency-input', true).first();
41207     },
41208     
41209     hiddenEl : function()
41210     {
41211         return this.el.select('input.hidden-number-input',true).first();
41212     }
41213     
41214 });