Roo/bootstrap/NavHeaderbar.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');                                 
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             cn.push(/*{
4092                 tag: 'div',
4093                 cls: 'navbar-header',
4094                 cn: [
4095                    */ {
4096                         tag: 'button',
4097                         type: 'button',
4098                         cls: 'navbar-toggle navbar-toggler',
4099                         'data-toggle': 'collapse',
4100                         cn: [
4101                             {
4102                                 tag: 'span',
4103                                 cls: 'sr-only',
4104                                 html: 'Toggle navigation'
4105                             },
4106                             {
4107                                 tag: 'span',
4108                                 cls: 'icon-bar navbar-toggler-icon'
4109                             },
4110                             {
4111                                 tag: 'span',
4112                                 cls: 'icon-bar'
4113                             },
4114                             {
4115                                 tag: 'span',
4116                                 cls: 'icon-bar'
4117                             }
4118                         ]
4119                     } /*
4120                 ]
4121             }*/);
4122         }
4123         
4124         cn.push({
4125             tag: 'div',
4126             cls: 'collapse navbar-collapse',
4127             cn : []
4128         });
4129         
4130         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4131         
4132         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4133             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4134             
4135             // tag can override this..
4136             
4137             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4138         }
4139         
4140         if (this.brand !== '') {
4141             //cn[0].cn.unshift
4142             cn.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4143                 tag: 'a',
4144                 href: this.brand_href ? this.brand_href : '#',
4145                 cls: 'navbar-brand',
4146                 cn: [
4147                 this.brand
4148                 ]
4149             });
4150         }
4151         
4152         if(this.main){
4153             cfg.cls += ' main-nav';
4154         }
4155         
4156         
4157         return cfg;
4158
4159         
4160     },
4161     getHeaderChildContainer : function()
4162     {
4163         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4164             return this.el.select('.navbar-header',true).first();
4165         }
4166         
4167         return this.getChildContainer();
4168     },
4169     
4170     
4171     initEvents : function()
4172     {
4173         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4174         
4175         if (this.autohide) {
4176             
4177             var prevScroll = 0;
4178             var ft = this.el;
4179             
4180             Roo.get(document).on('scroll',function(e) {
4181                 var ns = Roo.get(document).getScroll().top;
4182                 var os = prevScroll;
4183                 prevScroll = ns;
4184                 
4185                 if(ns > os){
4186                     ft.removeClass('slideDown');
4187                     ft.addClass('slideUp');
4188                     return;
4189                 }
4190                 ft.removeClass('slideUp');
4191                 ft.addClass('slideDown');
4192                  
4193               
4194           },this);
4195         }
4196     }    
4197     
4198 });
4199
4200
4201
4202  
4203
4204  /*
4205  * - LGPL
4206  *
4207  * navbar
4208  * 
4209  */
4210
4211 /**
4212  * @class Roo.bootstrap.NavSidebar
4213  * @extends Roo.bootstrap.Navbar
4214  * Bootstrap Sidebar class
4215  * 
4216  * @constructor
4217  * Create a new Sidebar
4218  * @param {Object} config The config object
4219  */
4220
4221
4222 Roo.bootstrap.NavSidebar = function(config){
4223     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4224 };
4225
4226 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4227     
4228     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4229     
4230     getAutoCreate : function(){
4231         
4232         
4233         return  {
4234             tag: 'div',
4235             cls: 'sidebar sidebar-nav'
4236         };
4237     
4238         
4239     }
4240     
4241     
4242     
4243 });
4244
4245
4246
4247  
4248
4249  /*
4250  * - LGPL
4251  *
4252  * nav group
4253  * 
4254  */
4255
4256 /**
4257  * @class Roo.bootstrap.NavGroup
4258  * @extends Roo.bootstrap.Component
4259  * Bootstrap NavGroup class
4260  * @cfg {String} align (left|right)
4261  * @cfg {Boolean} inverse
4262  * @cfg {String} type (nav|pills|tab) default nav
4263  * @cfg {String} navId - reference Id for navbar.
4264
4265  * 
4266  * @constructor
4267  * Create a new nav group
4268  * @param {Object} config The config object
4269  */
4270
4271 Roo.bootstrap.NavGroup = function(config){
4272     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4273     this.navItems = [];
4274    
4275     Roo.bootstrap.NavGroup.register(this);
4276      this.addEvents({
4277         /**
4278              * @event changed
4279              * Fires when the active item changes
4280              * @param {Roo.bootstrap.NavGroup} this
4281              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4282              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4283          */
4284         'changed': true
4285      });
4286     
4287 };
4288
4289 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4290     
4291     align: '',
4292     inverse: false,
4293     form: false,
4294     type: 'nav',
4295     navId : '',
4296     // private
4297     
4298     navItems : false, 
4299     
4300     getAutoCreate : function()
4301     {
4302         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4303         
4304         cfg = {
4305             tag : 'ul',
4306             cls: 'nav' 
4307         };
4308         
4309         if (['tabs','pills'].indexOf(this.type)!==-1) {
4310             cfg.cls += ' nav-' + this.type
4311         } else {
4312             if (this.type!=='nav') {
4313                 Roo.log('nav type must be nav/tabs/pills')
4314             }
4315             cfg.cls += ' navbar-nav'
4316         }
4317         
4318         if (this.parent() && this.parent().sidebar) {
4319             cfg = {
4320                 tag: 'ul',
4321                 cls: 'dashboard-menu sidebar-menu'
4322             };
4323             
4324             return cfg;
4325         }
4326         
4327         if (this.form === true) {
4328             cfg = {
4329                 tag: 'form',
4330                 cls: 'navbar-form'
4331             };
4332             
4333             if (this.align === 'right') {
4334                 cfg.cls += ' navbar-right ml-md-auto';
4335             } else {
4336                 cfg.cls += ' navbar-left';
4337             }
4338         }
4339         
4340         if (this.align === 'right') {
4341             cfg.cls += ' navbar-right ml-md-auto';
4342         } else {
4343             cfg.cls += ' mr-auto';
4344         }
4345         
4346         if (this.inverse) {
4347             cfg.cls += ' navbar-inverse';
4348             
4349         }
4350         
4351         
4352         return cfg;
4353     },
4354     /**
4355     * sets the active Navigation item
4356     * @param {Roo.bootstrap.NavItem} the new current navitem
4357     */
4358     setActiveItem : function(item)
4359     {
4360         var prev = false;
4361         Roo.each(this.navItems, function(v){
4362             if (v == item) {
4363                 return ;
4364             }
4365             if (v.isActive()) {
4366                 v.setActive(false, true);
4367                 prev = v;
4368                 
4369             }
4370             
4371         });
4372
4373         item.setActive(true, true);
4374         this.fireEvent('changed', this, item, prev);
4375         
4376         
4377     },
4378     /**
4379     * gets the active Navigation item
4380     * @return {Roo.bootstrap.NavItem} the current navitem
4381     */
4382     getActive : function()
4383     {
4384         
4385         var prev = false;
4386         Roo.each(this.navItems, function(v){
4387             
4388             if (v.isActive()) {
4389                 prev = v;
4390                 
4391             }
4392             
4393         });
4394         return prev;
4395     },
4396     
4397     indexOfNav : function()
4398     {
4399         
4400         var prev = false;
4401         Roo.each(this.navItems, function(v,i){
4402             
4403             if (v.isActive()) {
4404                 prev = i;
4405                 
4406             }
4407             
4408         });
4409         return prev;
4410     },
4411     /**
4412     * adds a Navigation item
4413     * @param {Roo.bootstrap.NavItem} the navitem to add
4414     */
4415     addItem : function(cfg)
4416     {
4417         var cn = new Roo.bootstrap.NavItem(cfg);
4418         this.register(cn);
4419         cn.parentId = this.id;
4420         cn.onRender(this.el, null);
4421         return cn;
4422     },
4423     /**
4424     * register a Navigation item
4425     * @param {Roo.bootstrap.NavItem} the navitem to add
4426     */
4427     register : function(item)
4428     {
4429         this.navItems.push( item);
4430         item.navId = this.navId;
4431     
4432     },
4433     
4434     /**
4435     * clear all the Navigation item
4436     */
4437    
4438     clearAll : function()
4439     {
4440         this.navItems = [];
4441         this.el.dom.innerHTML = '';
4442     },
4443     
4444     getNavItem: function(tabId)
4445     {
4446         var ret = false;
4447         Roo.each(this.navItems, function(e) {
4448             if (e.tabId == tabId) {
4449                ret =  e;
4450                return false;
4451             }
4452             return true;
4453             
4454         });
4455         return ret;
4456     },
4457     
4458     setActiveNext : function()
4459     {
4460         var i = this.indexOfNav(this.getActive());
4461         if (i > this.navItems.length) {
4462             return;
4463         }
4464         this.setActiveItem(this.navItems[i+1]);
4465     },
4466     setActivePrev : function()
4467     {
4468         var i = this.indexOfNav(this.getActive());
4469         if (i  < 1) {
4470             return;
4471         }
4472         this.setActiveItem(this.navItems[i-1]);
4473     },
4474     clearWasActive : function(except) {
4475         Roo.each(this.navItems, function(e) {
4476             if (e.tabId != except.tabId && e.was_active) {
4477                e.was_active = false;
4478                return false;
4479             }
4480             return true;
4481             
4482         });
4483     },
4484     getWasActive : function ()
4485     {
4486         var r = false;
4487         Roo.each(this.navItems, function(e) {
4488             if (e.was_active) {
4489                r = e;
4490                return false;
4491             }
4492             return true;
4493             
4494         });
4495         return r;
4496     }
4497     
4498     
4499 });
4500
4501  
4502 Roo.apply(Roo.bootstrap.NavGroup, {
4503     
4504     groups: {},
4505      /**
4506     * register a Navigation Group
4507     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4508     */
4509     register : function(navgrp)
4510     {
4511         this.groups[navgrp.navId] = navgrp;
4512         
4513     },
4514     /**
4515     * fetch a Navigation Group based on the navigation ID
4516     * @param {string} the navgroup to add
4517     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4518     */
4519     get: function(navId) {
4520         if (typeof(this.groups[navId]) == 'undefined') {
4521             return false;
4522             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4523         }
4524         return this.groups[navId] ;
4525     }
4526     
4527     
4528     
4529 });
4530
4531  /*
4532  * - LGPL
4533  *
4534  * row
4535  * 
4536  */
4537
4538 /**
4539  * @class Roo.bootstrap.NavItem
4540  * @extends Roo.bootstrap.Component
4541  * Bootstrap Navbar.NavItem class
4542  * @cfg {String} href  link to
4543  * @cfg {String} html content of button
4544  * @cfg {String} badge text inside badge
4545  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4546  * @cfg {String} glyphicon DEPRICATED - use fa
4547  * @cfg {String} icon DEPRICATED - use fa
4548  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4549  * @cfg {Boolean} active Is item active
4550  * @cfg {Boolean} disabled Is item disabled
4551  
4552  * @cfg {Boolean} preventDefault (true | false) default false
4553  * @cfg {String} tabId the tab that this item activates.
4554  * @cfg {String} tagtype (a|span) render as a href or span?
4555  * @cfg {Boolean} animateRef (true|false) link to element default false  
4556   
4557  * @constructor
4558  * Create a new Navbar Item
4559  * @param {Object} config The config object
4560  */
4561 Roo.bootstrap.NavItem = function(config){
4562     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4563     this.addEvents({
4564         // raw events
4565         /**
4566          * @event click
4567          * The raw click event for the entire grid.
4568          * @param {Roo.EventObject} e
4569          */
4570         "click" : true,
4571          /**
4572             * @event changed
4573             * Fires when the active item active state changes
4574             * @param {Roo.bootstrap.NavItem} this
4575             * @param {boolean} state the new state
4576              
4577          */
4578         'changed': true,
4579         /**
4580             * @event scrollto
4581             * Fires when scroll to element
4582             * @param {Roo.bootstrap.NavItem} this
4583             * @param {Object} options
4584             * @param {Roo.EventObject} e
4585              
4586          */
4587         'scrollto': true
4588     });
4589    
4590 };
4591
4592 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4593     
4594     href: false,
4595     html: '',
4596     badge: '',
4597     icon: false,
4598     fa : false,
4599     glyphicon: false,
4600     active: false,
4601     preventDefault : false,
4602     tabId : false,
4603     tagtype : 'a',
4604     disabled : false,
4605     animateRef : false,
4606     was_active : false,
4607     
4608     getAutoCreate : function(){
4609          
4610         var cfg = {
4611             tag: 'li',
4612             cls: 'nav-item'
4613             
4614         };
4615         
4616         if (this.active) {
4617             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4618         }
4619         if (this.disabled) {
4620             cfg.cls += ' disabled';
4621         }
4622         
4623         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4624             cfg.cn = [
4625                 {
4626                     tag: this.tagtype,
4627                     href : this.href || "#",
4628                     html: this.html || ''
4629                 }
4630             ];
4631             if (this.tagtype == 'a') {
4632                 cfg.cn[0].cls = 'nav-link';
4633             }
4634             if (this.icon) {
4635                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4636             }
4637             if (this.fa) {
4638                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4639             }
4640             if(this.glyphicon) {
4641                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4642             }
4643             
4644             if (this.menu) {
4645                 
4646                 cfg.cn[0].html += " <span class='caret'></span>";
4647              
4648             }
4649             
4650             if (this.badge !== '') {
4651                  
4652                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4653             }
4654         }
4655         
4656         
4657         
4658         return cfg;
4659     },
4660     initEvents: function() 
4661     {
4662         if (typeof (this.menu) != 'undefined') {
4663             this.menu.parentType = this.xtype;
4664             this.menu.triggerEl = this.el;
4665             this.menu = this.addxtype(Roo.apply({}, this.menu));
4666         }
4667         
4668         this.el.select('a',true).on('click', this.onClick, this);
4669         
4670         if(this.tagtype == 'span'){
4671             this.el.select('span',true).on('click', this.onClick, this);
4672         }
4673        
4674         // at this point parent should be available..
4675         this.parent().register(this);
4676     },
4677     
4678     onClick : function(e)
4679     {
4680         if (e.getTarget('.dropdown-menu-item')) {
4681             // did you click on a menu itemm.... - then don't trigger onclick..
4682             return;
4683         }
4684         
4685         if(
4686                 this.preventDefault || 
4687                 this.href == '#' 
4688         ){
4689             Roo.log("NavItem - prevent Default?");
4690             e.preventDefault();
4691         }
4692         
4693         if (this.disabled) {
4694             return;
4695         }
4696         
4697         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4698         if (tg && tg.transition) {
4699             Roo.log("waiting for the transitionend");
4700             return;
4701         }
4702         
4703         
4704         
4705         //Roo.log("fire event clicked");
4706         if(this.fireEvent('click', this, e) === false){
4707             return;
4708         };
4709         
4710         if(this.tagtype == 'span'){
4711             return;
4712         }
4713         
4714         //Roo.log(this.href);
4715         var ael = this.el.select('a',true).first();
4716         //Roo.log(ael);
4717         
4718         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4719             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4720             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4721                 return; // ignore... - it's a 'hash' to another page.
4722             }
4723             Roo.log("NavItem - prevent Default?");
4724             e.preventDefault();
4725             this.scrollToElement(e);
4726         }
4727         
4728         
4729         var p =  this.parent();
4730    
4731         if (['tabs','pills'].indexOf(p.type)!==-1) {
4732             if (typeof(p.setActiveItem) !== 'undefined') {
4733                 p.setActiveItem(this);
4734             }
4735         }
4736         
4737         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4738         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4739             // remove the collapsed menu expand...
4740             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4741         }
4742     },
4743     
4744     isActive: function () {
4745         return this.active
4746     },
4747     setActive : function(state, fire, is_was_active)
4748     {
4749         if (this.active && !state && this.navId) {
4750             this.was_active = true;
4751             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4752             if (nv) {
4753                 nv.clearWasActive(this);
4754             }
4755             
4756         }
4757         this.active = state;
4758         
4759         if (!state ) {
4760             this.el.removeClass('active');
4761         } else if (!this.el.hasClass('active')) {
4762             this.el.addClass('active');
4763         }
4764         if (fire) {
4765             this.fireEvent('changed', this, state);
4766         }
4767         
4768         // show a panel if it's registered and related..
4769         
4770         if (!this.navId || !this.tabId || !state || is_was_active) {
4771             return;
4772         }
4773         
4774         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4775         if (!tg) {
4776             return;
4777         }
4778         var pan = tg.getPanelByName(this.tabId);
4779         if (!pan) {
4780             return;
4781         }
4782         // if we can not flip to new panel - go back to old nav highlight..
4783         if (false == tg.showPanel(pan)) {
4784             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4785             if (nv) {
4786                 var onav = nv.getWasActive();
4787                 if (onav) {
4788                     onav.setActive(true, false, true);
4789                 }
4790             }
4791             
4792         }
4793         
4794         
4795         
4796     },
4797      // this should not be here...
4798     setDisabled : function(state)
4799     {
4800         this.disabled = state;
4801         if (!state ) {
4802             this.el.removeClass('disabled');
4803         } else if (!this.el.hasClass('disabled')) {
4804             this.el.addClass('disabled');
4805         }
4806         
4807     },
4808     
4809     /**
4810      * Fetch the element to display the tooltip on.
4811      * @return {Roo.Element} defaults to this.el
4812      */
4813     tooltipEl : function()
4814     {
4815         return this.el.select('' + this.tagtype + '', true).first();
4816     },
4817     
4818     scrollToElement : function(e)
4819     {
4820         var c = document.body;
4821         
4822         /*
4823          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4824          */
4825         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4826             c = document.documentElement;
4827         }
4828         
4829         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4830         
4831         if(!target){
4832             return;
4833         }
4834
4835         var o = target.calcOffsetsTo(c);
4836         
4837         var options = {
4838             target : target,
4839             value : o[1]
4840         };
4841         
4842         this.fireEvent('scrollto', this, options, e);
4843         
4844         Roo.get(c).scrollTo('top', options.value, true);
4845         
4846         return;
4847     }
4848 });
4849  
4850
4851  /*
4852  * - LGPL
4853  *
4854  * sidebar item
4855  *
4856  *  li
4857  *    <span> icon </span>
4858  *    <span> text </span>
4859  *    <span>badge </span>
4860  */
4861
4862 /**
4863  * @class Roo.bootstrap.NavSidebarItem
4864  * @extends Roo.bootstrap.NavItem
4865  * Bootstrap Navbar.NavSidebarItem class
4866  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4867  * {Boolean} open is the menu open
4868  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4869  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4870  * {String} buttonSize (sm|md|lg)the extra classes for the button
4871  * {Boolean} showArrow show arrow next to the text (default true)
4872  * @constructor
4873  * Create a new Navbar Button
4874  * @param {Object} config The config object
4875  */
4876 Roo.bootstrap.NavSidebarItem = function(config){
4877     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4878     this.addEvents({
4879         // raw events
4880         /**
4881          * @event click
4882          * The raw click event for the entire grid.
4883          * @param {Roo.EventObject} e
4884          */
4885         "click" : true,
4886          /**
4887             * @event changed
4888             * Fires when the active item active state changes
4889             * @param {Roo.bootstrap.NavSidebarItem} this
4890             * @param {boolean} state the new state
4891              
4892          */
4893         'changed': true
4894     });
4895    
4896 };
4897
4898 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4899     
4900     badgeWeight : 'default',
4901     
4902     open: false,
4903     
4904     buttonView : false,
4905     
4906     buttonWeight : 'default',
4907     
4908     buttonSize : 'md',
4909     
4910     showArrow : true,
4911     
4912     getAutoCreate : function(){
4913         
4914         
4915         var a = {
4916                 tag: 'a',
4917                 href : this.href || '#',
4918                 cls: '',
4919                 html : '',
4920                 cn : []
4921         };
4922         
4923         if(this.buttonView){
4924             a = {
4925                 tag: 'button',
4926                 href : this.href || '#',
4927                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4928                 html : this.html,
4929                 cn : []
4930             };
4931         }
4932         
4933         var cfg = {
4934             tag: 'li',
4935             cls: '',
4936             cn: [ a ]
4937         };
4938         
4939         if (this.active) {
4940             cfg.cls += ' active';
4941         }
4942         
4943         if (this.disabled) {
4944             cfg.cls += ' disabled';
4945         }
4946         if (this.open) {
4947             cfg.cls += ' open x-open';
4948         }
4949         // left icon..
4950         if (this.glyphicon || this.icon) {
4951             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4952             a.cn.push({ tag : 'i', cls : c }) ;
4953         }
4954         
4955         if(!this.buttonView){
4956             var span = {
4957                 tag: 'span',
4958                 html : this.html || ''
4959             };
4960
4961             a.cn.push(span);
4962             
4963         }
4964         
4965         if (this.badge !== '') {
4966             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4967         }
4968         
4969         if (this.menu) {
4970             
4971             if(this.showArrow){
4972                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4973             }
4974             
4975             a.cls += ' dropdown-toggle treeview' ;
4976         }
4977         
4978         return cfg;
4979     },
4980     
4981     initEvents : function()
4982     { 
4983         if (typeof (this.menu) != 'undefined') {
4984             this.menu.parentType = this.xtype;
4985             this.menu.triggerEl = this.el;
4986             this.menu = this.addxtype(Roo.apply({}, this.menu));
4987         }
4988         
4989         this.el.on('click', this.onClick, this);
4990         
4991         if(this.badge !== ''){
4992             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4993         }
4994         
4995     },
4996     
4997     onClick : function(e)
4998     {
4999         if(this.disabled){
5000             e.preventDefault();
5001             return;
5002         }
5003         
5004         if(this.preventDefault){
5005             e.preventDefault();
5006         }
5007         
5008         this.fireEvent('click', this);
5009     },
5010     
5011     disable : function()
5012     {
5013         this.setDisabled(true);
5014     },
5015     
5016     enable : function()
5017     {
5018         this.setDisabled(false);
5019     },
5020     
5021     setDisabled : function(state)
5022     {
5023         if(this.disabled == state){
5024             return;
5025         }
5026         
5027         this.disabled = state;
5028         
5029         if (state) {
5030             this.el.addClass('disabled');
5031             return;
5032         }
5033         
5034         this.el.removeClass('disabled');
5035         
5036         return;
5037     },
5038     
5039     setActive : function(state)
5040     {
5041         if(this.active == state){
5042             return;
5043         }
5044         
5045         this.active = state;
5046         
5047         if (state) {
5048             this.el.addClass('active');
5049             return;
5050         }
5051         
5052         this.el.removeClass('active');
5053         
5054         return;
5055     },
5056     
5057     isActive: function () 
5058     {
5059         return this.active;
5060     },
5061     
5062     setBadge : function(str)
5063     {
5064         if(!this.badgeEl){
5065             return;
5066         }
5067         
5068         this.badgeEl.dom.innerHTML = str;
5069     }
5070     
5071    
5072      
5073  
5074 });
5075  
5076
5077  /*
5078  * - LGPL
5079  *
5080  * row
5081  * 
5082  */
5083
5084 /**
5085  * @class Roo.bootstrap.Row
5086  * @extends Roo.bootstrap.Component
5087  * Bootstrap Row class (contains columns...)
5088  * 
5089  * @constructor
5090  * Create a new Row
5091  * @param {Object} config The config object
5092  */
5093
5094 Roo.bootstrap.Row = function(config){
5095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5096 };
5097
5098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5099     
5100     getAutoCreate : function(){
5101        return {
5102             cls: 'row clearfix'
5103        };
5104     }
5105     
5106     
5107 });
5108
5109  
5110
5111  /*
5112  * - LGPL
5113  *
5114  * element
5115  * 
5116  */
5117
5118 /**
5119  * @class Roo.bootstrap.Element
5120  * @extends Roo.bootstrap.Component
5121  * Bootstrap Element class
5122  * @cfg {String} html contents of the element
5123  * @cfg {String} tag tag of the element
5124  * @cfg {String} cls class of the element
5125  * @cfg {Boolean} preventDefault (true|false) default false
5126  * @cfg {Boolean} clickable (true|false) default false
5127  * 
5128  * @constructor
5129  * Create a new Element
5130  * @param {Object} config The config object
5131  */
5132
5133 Roo.bootstrap.Element = function(config){
5134     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5135     
5136     this.addEvents({
5137         // raw events
5138         /**
5139          * @event click
5140          * When a element is chick
5141          * @param {Roo.bootstrap.Element} this
5142          * @param {Roo.EventObject} e
5143          */
5144         "click" : true
5145     });
5146 };
5147
5148 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5149     
5150     tag: 'div',
5151     cls: '',
5152     html: '',
5153     preventDefault: false, 
5154     clickable: false,
5155     
5156     getAutoCreate : function(){
5157         
5158         var cfg = {
5159             tag: this.tag,
5160             // cls: this.cls, double assign in parent class Component.js :: onRender
5161             html: this.html
5162         };
5163         
5164         return cfg;
5165     },
5166     
5167     initEvents: function() 
5168     {
5169         Roo.bootstrap.Element.superclass.initEvents.call(this);
5170         
5171         if(this.clickable){
5172             this.el.on('click', this.onClick, this);
5173         }
5174         
5175     },
5176     
5177     onClick : function(e)
5178     {
5179         if(this.preventDefault){
5180             e.preventDefault();
5181         }
5182         
5183         this.fireEvent('click', this, e);
5184     },
5185     
5186     getValue : function()
5187     {
5188         return this.el.dom.innerHTML;
5189     },
5190     
5191     setValue : function(value)
5192     {
5193         this.el.dom.innerHTML = value;
5194     }
5195    
5196 });
5197
5198  
5199
5200  /*
5201  * - LGPL
5202  *
5203  * pagination
5204  * 
5205  */
5206
5207 /**
5208  * @class Roo.bootstrap.Pagination
5209  * @extends Roo.bootstrap.Component
5210  * Bootstrap Pagination class
5211  * @cfg {String} size xs | sm | md | lg
5212  * @cfg {Boolean} inverse false | true
5213  * 
5214  * @constructor
5215  * Create a new Pagination
5216  * @param {Object} config The config object
5217  */
5218
5219 Roo.bootstrap.Pagination = function(config){
5220     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5224     
5225     cls: false,
5226     size: false,
5227     inverse: false,
5228     
5229     getAutoCreate : function(){
5230         var cfg = {
5231             tag: 'ul',
5232                 cls: 'pagination'
5233         };
5234         if (this.inverse) {
5235             cfg.cls += ' inverse';
5236         }
5237         if (this.html) {
5238             cfg.html=this.html;
5239         }
5240         if (this.cls) {
5241             cfg.cls += " " + this.cls;
5242         }
5243         return cfg;
5244     }
5245    
5246 });
5247
5248  
5249
5250  /*
5251  * - LGPL
5252  *
5253  * Pagination item
5254  * 
5255  */
5256
5257
5258 /**
5259  * @class Roo.bootstrap.PaginationItem
5260  * @extends Roo.bootstrap.Component
5261  * Bootstrap PaginationItem class
5262  * @cfg {String} html text
5263  * @cfg {String} href the link
5264  * @cfg {Boolean} preventDefault (true | false) default true
5265  * @cfg {Boolean} active (true | false) default false
5266  * @cfg {Boolean} disabled default false
5267  * 
5268  * 
5269  * @constructor
5270  * Create a new PaginationItem
5271  * @param {Object} config The config object
5272  */
5273
5274
5275 Roo.bootstrap.PaginationItem = function(config){
5276     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5277     this.addEvents({
5278         // raw events
5279         /**
5280          * @event click
5281          * The raw click event for the entire grid.
5282          * @param {Roo.EventObject} e
5283          */
5284         "click" : true
5285     });
5286 };
5287
5288 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5289     
5290     href : false,
5291     html : false,
5292     preventDefault: true,
5293     active : false,
5294     cls : false,
5295     disabled: false,
5296     
5297     getAutoCreate : function(){
5298         var cfg= {
5299             tag: 'li',
5300             cn: [
5301                 {
5302                     tag : 'a',
5303                     href : this.href ? this.href : '#',
5304                     html : this.html ? this.html : ''
5305                 }
5306             ]
5307         };
5308         
5309         if(this.cls){
5310             cfg.cls = this.cls;
5311         }
5312         
5313         if(this.disabled){
5314             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5315         }
5316         
5317         if(this.active){
5318             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5319         }
5320         
5321         return cfg;
5322     },
5323     
5324     initEvents: function() {
5325         
5326         this.el.on('click', this.onClick, this);
5327         
5328     },
5329     onClick : function(e)
5330     {
5331         Roo.log('PaginationItem on click ');
5332         if(this.preventDefault){
5333             e.preventDefault();
5334         }
5335         
5336         if(this.disabled){
5337             return;
5338         }
5339         
5340         this.fireEvent('click', this, e);
5341     }
5342    
5343 });
5344
5345  
5346
5347  /*
5348  * - LGPL
5349  *
5350  * slider
5351  * 
5352  */
5353
5354
5355 /**
5356  * @class Roo.bootstrap.Slider
5357  * @extends Roo.bootstrap.Component
5358  * Bootstrap Slider class
5359  *    
5360  * @constructor
5361  * Create a new Slider
5362  * @param {Object} config The config object
5363  */
5364
5365 Roo.bootstrap.Slider = function(config){
5366     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5367 };
5368
5369 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5370     
5371     getAutoCreate : function(){
5372         
5373         var cfg = {
5374             tag: 'div',
5375             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5376             cn: [
5377                 {
5378                     tag: 'a',
5379                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5380                 }
5381             ]
5382         };
5383         
5384         return cfg;
5385     }
5386    
5387 });
5388
5389  /*
5390  * Based on:
5391  * Ext JS Library 1.1.1
5392  * Copyright(c) 2006-2007, Ext JS, LLC.
5393  *
5394  * Originally Released Under LGPL - original licence link has changed is not relivant.
5395  *
5396  * Fork - LGPL
5397  * <script type="text/javascript">
5398  */
5399  
5400
5401 /**
5402  * @class Roo.grid.ColumnModel
5403  * @extends Roo.util.Observable
5404  * This is the default implementation of a ColumnModel used by the Grid. It defines
5405  * the columns in the grid.
5406  * <br>Usage:<br>
5407  <pre><code>
5408  var colModel = new Roo.grid.ColumnModel([
5409         {header: "Ticker", width: 60, sortable: true, locked: true},
5410         {header: "Company Name", width: 150, sortable: true},
5411         {header: "Market Cap.", width: 100, sortable: true},
5412         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5413         {header: "Employees", width: 100, sortable: true, resizable: false}
5414  ]);
5415  </code></pre>
5416  * <p>
5417  
5418  * The config options listed for this class are options which may appear in each
5419  * individual column definition.
5420  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5421  * @constructor
5422  * @param {Object} config An Array of column config objects. See this class's
5423  * config objects for details.
5424 */
5425 Roo.grid.ColumnModel = function(config){
5426         /**
5427      * The config passed into the constructor
5428      */
5429     this.config = config;
5430     this.lookup = {};
5431
5432     // if no id, create one
5433     // if the column does not have a dataIndex mapping,
5434     // map it to the order it is in the config
5435     for(var i = 0, len = config.length; i < len; i++){
5436         var c = config[i];
5437         if(typeof c.dataIndex == "undefined"){
5438             c.dataIndex = i;
5439         }
5440         if(typeof c.renderer == "string"){
5441             c.renderer = Roo.util.Format[c.renderer];
5442         }
5443         if(typeof c.id == "undefined"){
5444             c.id = Roo.id();
5445         }
5446         if(c.editor && c.editor.xtype){
5447             c.editor  = Roo.factory(c.editor, Roo.grid);
5448         }
5449         if(c.editor && c.editor.isFormField){
5450             c.editor = new Roo.grid.GridEditor(c.editor);
5451         }
5452         this.lookup[c.id] = c;
5453     }
5454
5455     /**
5456      * The width of columns which have no width specified (defaults to 100)
5457      * @type Number
5458      */
5459     this.defaultWidth = 100;
5460
5461     /**
5462      * Default sortable of columns which have no sortable specified (defaults to false)
5463      * @type Boolean
5464      */
5465     this.defaultSortable = false;
5466
5467     this.addEvents({
5468         /**
5469              * @event widthchange
5470              * Fires when the width of a column changes.
5471              * @param {ColumnModel} this
5472              * @param {Number} columnIndex The column index
5473              * @param {Number} newWidth The new width
5474              */
5475             "widthchange": true,
5476         /**
5477              * @event headerchange
5478              * Fires when the text of a header changes.
5479              * @param {ColumnModel} this
5480              * @param {Number} columnIndex The column index
5481              * @param {Number} newText The new header text
5482              */
5483             "headerchange": true,
5484         /**
5485              * @event hiddenchange
5486              * Fires when a column is hidden or "unhidden".
5487              * @param {ColumnModel} this
5488              * @param {Number} columnIndex The column index
5489              * @param {Boolean} hidden true if hidden, false otherwise
5490              */
5491             "hiddenchange": true,
5492             /**
5493          * @event columnmoved
5494          * Fires when a column is moved.
5495          * @param {ColumnModel} this
5496          * @param {Number} oldIndex
5497          * @param {Number} newIndex
5498          */
5499         "columnmoved" : true,
5500         /**
5501          * @event columlockchange
5502          * Fires when a column's locked state is changed
5503          * @param {ColumnModel} this
5504          * @param {Number} colIndex
5505          * @param {Boolean} locked true if locked
5506          */
5507         "columnlockchange" : true
5508     });
5509     Roo.grid.ColumnModel.superclass.constructor.call(this);
5510 };
5511 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5512     /**
5513      * @cfg {String} header The header text to display in the Grid view.
5514      */
5515     /**
5516      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5517      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5518      * specified, the column's index is used as an index into the Record's data Array.
5519      */
5520     /**
5521      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5522      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5523      */
5524     /**
5525      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5526      * Defaults to the value of the {@link #defaultSortable} property.
5527      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5528      */
5529     /**
5530      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5531      */
5532     /**
5533      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5534      */
5535     /**
5536      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5537      */
5538     /**
5539      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5540      */
5541     /**
5542      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5543      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5544      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5545      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5546      */
5547        /**
5548      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5549      */
5550     /**
5551      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5552      */
5553     /**
5554      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5555      */
5556     /**
5557      * @cfg {String} cursor (Optional)
5558      */
5559     /**
5560      * @cfg {String} tooltip (Optional)
5561      */
5562     /**
5563      * @cfg {Number} xs (Optional)
5564      */
5565     /**
5566      * @cfg {Number} sm (Optional)
5567      */
5568     /**
5569      * @cfg {Number} md (Optional)
5570      */
5571     /**
5572      * @cfg {Number} lg (Optional)
5573      */
5574     /**
5575      * Returns the id of the column at the specified index.
5576      * @param {Number} index The column index
5577      * @return {String} the id
5578      */
5579     getColumnId : function(index){
5580         return this.config[index].id;
5581     },
5582
5583     /**
5584      * Returns the column for a specified id.
5585      * @param {String} id The column id
5586      * @return {Object} the column
5587      */
5588     getColumnById : function(id){
5589         return this.lookup[id];
5590     },
5591
5592     
5593     /**
5594      * Returns the column for a specified dataIndex.
5595      * @param {String} dataIndex The column dataIndex
5596      * @return {Object|Boolean} the column or false if not found
5597      */
5598     getColumnByDataIndex: function(dataIndex){
5599         var index = this.findColumnIndex(dataIndex);
5600         return index > -1 ? this.config[index] : false;
5601     },
5602     
5603     /**
5604      * Returns the index for a specified column id.
5605      * @param {String} id The column id
5606      * @return {Number} the index, or -1 if not found
5607      */
5608     getIndexById : function(id){
5609         for(var i = 0, len = this.config.length; i < len; i++){
5610             if(this.config[i].id == id){
5611                 return i;
5612             }
5613         }
5614         return -1;
5615     },
5616     
5617     /**
5618      * Returns the index for a specified column dataIndex.
5619      * @param {String} dataIndex The column dataIndex
5620      * @return {Number} the index, or -1 if not found
5621      */
5622     
5623     findColumnIndex : function(dataIndex){
5624         for(var i = 0, len = this.config.length; i < len; i++){
5625             if(this.config[i].dataIndex == dataIndex){
5626                 return i;
5627             }
5628         }
5629         return -1;
5630     },
5631     
5632     
5633     moveColumn : function(oldIndex, newIndex){
5634         var c = this.config[oldIndex];
5635         this.config.splice(oldIndex, 1);
5636         this.config.splice(newIndex, 0, c);
5637         this.dataMap = null;
5638         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5639     },
5640
5641     isLocked : function(colIndex){
5642         return this.config[colIndex].locked === true;
5643     },
5644
5645     setLocked : function(colIndex, value, suppressEvent){
5646         if(this.isLocked(colIndex) == value){
5647             return;
5648         }
5649         this.config[colIndex].locked = value;
5650         if(!suppressEvent){
5651             this.fireEvent("columnlockchange", this, colIndex, value);
5652         }
5653     },
5654
5655     getTotalLockedWidth : function(){
5656         var totalWidth = 0;
5657         for(var i = 0; i < this.config.length; i++){
5658             if(this.isLocked(i) && !this.isHidden(i)){
5659                 this.totalWidth += this.getColumnWidth(i);
5660             }
5661         }
5662         return totalWidth;
5663     },
5664
5665     getLockedCount : function(){
5666         for(var i = 0, len = this.config.length; i < len; i++){
5667             if(!this.isLocked(i)){
5668                 return i;
5669             }
5670         }
5671         
5672         return this.config.length;
5673     },
5674
5675     /**
5676      * Returns the number of columns.
5677      * @return {Number}
5678      */
5679     getColumnCount : function(visibleOnly){
5680         if(visibleOnly === true){
5681             var c = 0;
5682             for(var i = 0, len = this.config.length; i < len; i++){
5683                 if(!this.isHidden(i)){
5684                     c++;
5685                 }
5686             }
5687             return c;
5688         }
5689         return this.config.length;
5690     },
5691
5692     /**
5693      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5694      * @param {Function} fn
5695      * @param {Object} scope (optional)
5696      * @return {Array} result
5697      */
5698     getColumnsBy : function(fn, scope){
5699         var r = [];
5700         for(var i = 0, len = this.config.length; i < len; i++){
5701             var c = this.config[i];
5702             if(fn.call(scope||this, c, i) === true){
5703                 r[r.length] = c;
5704             }
5705         }
5706         return r;
5707     },
5708
5709     /**
5710      * Returns true if the specified column is sortable.
5711      * @param {Number} col The column index
5712      * @return {Boolean}
5713      */
5714     isSortable : function(col){
5715         if(typeof this.config[col].sortable == "undefined"){
5716             return this.defaultSortable;
5717         }
5718         return this.config[col].sortable;
5719     },
5720
5721     /**
5722      * Returns the rendering (formatting) function defined for the column.
5723      * @param {Number} col The column index.
5724      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5725      */
5726     getRenderer : function(col){
5727         if(!this.config[col].renderer){
5728             return Roo.grid.ColumnModel.defaultRenderer;
5729         }
5730         return this.config[col].renderer;
5731     },
5732
5733     /**
5734      * Sets the rendering (formatting) function for a column.
5735      * @param {Number} col The column index
5736      * @param {Function} fn The function to use to process the cell's raw data
5737      * to return HTML markup for the grid view. The render function is called with
5738      * the following parameters:<ul>
5739      * <li>Data value.</li>
5740      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5741      * <li>css A CSS style string to apply to the table cell.</li>
5742      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5743      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5744      * <li>Row index</li>
5745      * <li>Column index</li>
5746      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5747      */
5748     setRenderer : function(col, fn){
5749         this.config[col].renderer = fn;
5750     },
5751
5752     /**
5753      * Returns the width for the specified column.
5754      * @param {Number} col The column index
5755      * @return {Number}
5756      */
5757     getColumnWidth : function(col){
5758         return this.config[col].width * 1 || this.defaultWidth;
5759     },
5760
5761     /**
5762      * Sets the width for a column.
5763      * @param {Number} col The column index
5764      * @param {Number} width The new width
5765      */
5766     setColumnWidth : function(col, width, suppressEvent){
5767         this.config[col].width = width;
5768         this.totalWidth = null;
5769         if(!suppressEvent){
5770              this.fireEvent("widthchange", this, col, width);
5771         }
5772     },
5773
5774     /**
5775      * Returns the total width of all columns.
5776      * @param {Boolean} includeHidden True to include hidden column widths
5777      * @return {Number}
5778      */
5779     getTotalWidth : function(includeHidden){
5780         if(!this.totalWidth){
5781             this.totalWidth = 0;
5782             for(var i = 0, len = this.config.length; i < len; i++){
5783                 if(includeHidden || !this.isHidden(i)){
5784                     this.totalWidth += this.getColumnWidth(i);
5785                 }
5786             }
5787         }
5788         return this.totalWidth;
5789     },
5790
5791     /**
5792      * Returns the header for the specified column.
5793      * @param {Number} col The column index
5794      * @return {String}
5795      */
5796     getColumnHeader : function(col){
5797         return this.config[col].header;
5798     },
5799
5800     /**
5801      * Sets the header for a column.
5802      * @param {Number} col The column index
5803      * @param {String} header The new header
5804      */
5805     setColumnHeader : function(col, header){
5806         this.config[col].header = header;
5807         this.fireEvent("headerchange", this, col, header);
5808     },
5809
5810     /**
5811      * Returns the tooltip for the specified column.
5812      * @param {Number} col The column index
5813      * @return {String}
5814      */
5815     getColumnTooltip : function(col){
5816             return this.config[col].tooltip;
5817     },
5818     /**
5819      * Sets the tooltip for a column.
5820      * @param {Number} col The column index
5821      * @param {String} tooltip The new tooltip
5822      */
5823     setColumnTooltip : function(col, tooltip){
5824             this.config[col].tooltip = tooltip;
5825     },
5826
5827     /**
5828      * Returns the dataIndex for the specified column.
5829      * @param {Number} col The column index
5830      * @return {Number}
5831      */
5832     getDataIndex : function(col){
5833         return this.config[col].dataIndex;
5834     },
5835
5836     /**
5837      * Sets the dataIndex for a column.
5838      * @param {Number} col The column index
5839      * @param {Number} dataIndex The new dataIndex
5840      */
5841     setDataIndex : function(col, dataIndex){
5842         this.config[col].dataIndex = dataIndex;
5843     },
5844
5845     
5846     
5847     /**
5848      * Returns true if the cell is editable.
5849      * @param {Number} colIndex The column index
5850      * @param {Number} rowIndex The row index - this is nto actually used..?
5851      * @return {Boolean}
5852      */
5853     isCellEditable : function(colIndex, rowIndex){
5854         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5855     },
5856
5857     /**
5858      * Returns the editor defined for the cell/column.
5859      * return false or null to disable editing.
5860      * @param {Number} colIndex The column index
5861      * @param {Number} rowIndex The row index
5862      * @return {Object}
5863      */
5864     getCellEditor : function(colIndex, rowIndex){
5865         return this.config[colIndex].editor;
5866     },
5867
5868     /**
5869      * Sets if a column is editable.
5870      * @param {Number} col The column index
5871      * @param {Boolean} editable True if the column is editable
5872      */
5873     setEditable : function(col, editable){
5874         this.config[col].editable = editable;
5875     },
5876
5877
5878     /**
5879      * Returns true if the column is hidden.
5880      * @param {Number} colIndex The column index
5881      * @return {Boolean}
5882      */
5883     isHidden : function(colIndex){
5884         return this.config[colIndex].hidden;
5885     },
5886
5887
5888     /**
5889      * Returns true if the column width cannot be changed
5890      */
5891     isFixed : function(colIndex){
5892         return this.config[colIndex].fixed;
5893     },
5894
5895     /**
5896      * Returns true if the column can be resized
5897      * @return {Boolean}
5898      */
5899     isResizable : function(colIndex){
5900         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5901     },
5902     /**
5903      * Sets if a column is hidden.
5904      * @param {Number} colIndex The column index
5905      * @param {Boolean} hidden True if the column is hidden
5906      */
5907     setHidden : function(colIndex, hidden){
5908         this.config[colIndex].hidden = hidden;
5909         this.totalWidth = null;
5910         this.fireEvent("hiddenchange", this, colIndex, hidden);
5911     },
5912
5913     /**
5914      * Sets the editor for a column.
5915      * @param {Number} col The column index
5916      * @param {Object} editor The editor object
5917      */
5918     setEditor : function(col, editor){
5919         this.config[col].editor = editor;
5920     }
5921 });
5922
5923 Roo.grid.ColumnModel.defaultRenderer = function(value)
5924 {
5925     if(typeof value == "object") {
5926         return value;
5927     }
5928         if(typeof value == "string" && value.length < 1){
5929             return "&#160;";
5930         }
5931     
5932         return String.format("{0}", value);
5933 };
5934
5935 // Alias for backwards compatibility
5936 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5937 /*
5938  * Based on:
5939  * Ext JS Library 1.1.1
5940  * Copyright(c) 2006-2007, Ext JS, LLC.
5941  *
5942  * Originally Released Under LGPL - original licence link has changed is not relivant.
5943  *
5944  * Fork - LGPL
5945  * <script type="text/javascript">
5946  */
5947  
5948 /**
5949  * @class Roo.LoadMask
5950  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5951  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5952  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5953  * element's UpdateManager load indicator and will be destroyed after the initial load.
5954  * @constructor
5955  * Create a new LoadMask
5956  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5957  * @param {Object} config The config object
5958  */
5959 Roo.LoadMask = function(el, config){
5960     this.el = Roo.get(el);
5961     Roo.apply(this, config);
5962     if(this.store){
5963         this.store.on('beforeload', this.onBeforeLoad, this);
5964         this.store.on('load', this.onLoad, this);
5965         this.store.on('loadexception', this.onLoadException, this);
5966         this.removeMask = false;
5967     }else{
5968         var um = this.el.getUpdateManager();
5969         um.showLoadIndicator = false; // disable the default indicator
5970         um.on('beforeupdate', this.onBeforeLoad, this);
5971         um.on('update', this.onLoad, this);
5972         um.on('failure', this.onLoad, this);
5973         this.removeMask = true;
5974     }
5975 };
5976
5977 Roo.LoadMask.prototype = {
5978     /**
5979      * @cfg {Boolean} removeMask
5980      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5981      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5982      */
5983     /**
5984      * @cfg {String} msg
5985      * The text to display in a centered loading message box (defaults to 'Loading...')
5986      */
5987     msg : 'Loading...',
5988     /**
5989      * @cfg {String} msgCls
5990      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5991      */
5992     msgCls : 'x-mask-loading',
5993
5994     /**
5995      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5996      * @type Boolean
5997      */
5998     disabled: false,
5999
6000     /**
6001      * Disables the mask to prevent it from being displayed
6002      */
6003     disable : function(){
6004        this.disabled = true;
6005     },
6006
6007     /**
6008      * Enables the mask so that it can be displayed
6009      */
6010     enable : function(){
6011         this.disabled = false;
6012     },
6013     
6014     onLoadException : function()
6015     {
6016         Roo.log(arguments);
6017         
6018         if (typeof(arguments[3]) != 'undefined') {
6019             Roo.MessageBox.alert("Error loading",arguments[3]);
6020         } 
6021         /*
6022         try {
6023             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6024                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6025             }   
6026         } catch(e) {
6027             
6028         }
6029         */
6030     
6031         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6032     },
6033     // private
6034     onLoad : function()
6035     {
6036         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6037     },
6038
6039     // private
6040     onBeforeLoad : function(){
6041         if(!this.disabled){
6042             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6043         }
6044     },
6045
6046     // private
6047     destroy : function(){
6048         if(this.store){
6049             this.store.un('beforeload', this.onBeforeLoad, this);
6050             this.store.un('load', this.onLoad, this);
6051             this.store.un('loadexception', this.onLoadException, this);
6052         }else{
6053             var um = this.el.getUpdateManager();
6054             um.un('beforeupdate', this.onBeforeLoad, this);
6055             um.un('update', this.onLoad, this);
6056             um.un('failure', this.onLoad, this);
6057         }
6058     }
6059 };/*
6060  * - LGPL
6061  *
6062  * table
6063  * 
6064  */
6065
6066 /**
6067  * @class Roo.bootstrap.Table
6068  * @extends Roo.bootstrap.Component
6069  * Bootstrap Table class
6070  * @cfg {String} cls table class
6071  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6072  * @cfg {String} bgcolor Specifies the background color for a table
6073  * @cfg {Number} border Specifies whether the table cells should have borders or not
6074  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6075  * @cfg {Number} cellspacing Specifies the space between cells
6076  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6077  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6078  * @cfg {String} sortable Specifies that the table should be sortable
6079  * @cfg {String} summary Specifies a summary of the content of a table
6080  * @cfg {Number} width Specifies the width of a table
6081  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6082  * 
6083  * @cfg {boolean} striped Should the rows be alternative striped
6084  * @cfg {boolean} bordered Add borders to the table
6085  * @cfg {boolean} hover Add hover highlighting
6086  * @cfg {boolean} condensed Format condensed
6087  * @cfg {boolean} responsive Format condensed
6088  * @cfg {Boolean} loadMask (true|false) default false
6089  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6090  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6091  * @cfg {Boolean} rowSelection (true|false) default false
6092  * @cfg {Boolean} cellSelection (true|false) default false
6093  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6094  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6095  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6096  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6097  
6098  * 
6099  * @constructor
6100  * Create a new Table
6101  * @param {Object} config The config object
6102  */
6103
6104 Roo.bootstrap.Table = function(config){
6105     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6106     
6107   
6108     
6109     // BC...
6110     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6111     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6112     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6113     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6114     
6115     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6116     if (this.sm) {
6117         this.sm.grid = this;
6118         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6119         this.sm = this.selModel;
6120         this.sm.xmodule = this.xmodule || false;
6121     }
6122     
6123     if (this.cm && typeof(this.cm.config) == 'undefined') {
6124         this.colModel = new Roo.grid.ColumnModel(this.cm);
6125         this.cm = this.colModel;
6126         this.cm.xmodule = this.xmodule || false;
6127     }
6128     if (this.store) {
6129         this.store= Roo.factory(this.store, Roo.data);
6130         this.ds = this.store;
6131         this.ds.xmodule = this.xmodule || false;
6132          
6133     }
6134     if (this.footer && this.store) {
6135         this.footer.dataSource = this.ds;
6136         this.footer = Roo.factory(this.footer);
6137     }
6138     
6139     /** @private */
6140     this.addEvents({
6141         /**
6142          * @event cellclick
6143          * Fires when a cell is clicked
6144          * @param {Roo.bootstrap.Table} this
6145          * @param {Roo.Element} el
6146          * @param {Number} rowIndex
6147          * @param {Number} columnIndex
6148          * @param {Roo.EventObject} e
6149          */
6150         "cellclick" : true,
6151         /**
6152          * @event celldblclick
6153          * Fires when a cell is double clicked
6154          * @param {Roo.bootstrap.Table} this
6155          * @param {Roo.Element} el
6156          * @param {Number} rowIndex
6157          * @param {Number} columnIndex
6158          * @param {Roo.EventObject} e
6159          */
6160         "celldblclick" : true,
6161         /**
6162          * @event rowclick
6163          * Fires when a row is clicked
6164          * @param {Roo.bootstrap.Table} this
6165          * @param {Roo.Element} el
6166          * @param {Number} rowIndex
6167          * @param {Roo.EventObject} e
6168          */
6169         "rowclick" : true,
6170         /**
6171          * @event rowdblclick
6172          * Fires when a row is double clicked
6173          * @param {Roo.bootstrap.Table} this
6174          * @param {Roo.Element} el
6175          * @param {Number} rowIndex
6176          * @param {Roo.EventObject} e
6177          */
6178         "rowdblclick" : true,
6179         /**
6180          * @event mouseover
6181          * Fires when a mouseover occur
6182          * @param {Roo.bootstrap.Table} this
6183          * @param {Roo.Element} el
6184          * @param {Number} rowIndex
6185          * @param {Number} columnIndex
6186          * @param {Roo.EventObject} e
6187          */
6188         "mouseover" : true,
6189         /**
6190          * @event mouseout
6191          * Fires when a mouseout occur
6192          * @param {Roo.bootstrap.Table} this
6193          * @param {Roo.Element} el
6194          * @param {Number} rowIndex
6195          * @param {Number} columnIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "mouseout" : true,
6199         /**
6200          * @event rowclass
6201          * Fires when a row is rendered, so you can change add a style to it.
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6204          */
6205         'rowclass' : true,
6206           /**
6207          * @event rowsrendered
6208          * Fires when all the  rows have been rendered
6209          * @param {Roo.bootstrap.Table} this
6210          */
6211         'rowsrendered' : true,
6212         /**
6213          * @event contextmenu
6214          * The raw contextmenu event for the entire grid.
6215          * @param {Roo.EventObject} e
6216          */
6217         "contextmenu" : true,
6218         /**
6219          * @event rowcontextmenu
6220          * Fires when a row is right clicked
6221          * @param {Roo.bootstrap.Table} this
6222          * @param {Number} rowIndex
6223          * @param {Roo.EventObject} e
6224          */
6225         "rowcontextmenu" : true,
6226         /**
6227          * @event cellcontextmenu
6228          * Fires when a cell is right clicked
6229          * @param {Roo.bootstrap.Table} this
6230          * @param {Number} rowIndex
6231          * @param {Number} cellIndex
6232          * @param {Roo.EventObject} e
6233          */
6234          "cellcontextmenu" : true,
6235          /**
6236          * @event headercontextmenu
6237          * Fires when a header is right clicked
6238          * @param {Roo.bootstrap.Table} this
6239          * @param {Number} columnIndex
6240          * @param {Roo.EventObject} e
6241          */
6242         "headercontextmenu" : true
6243     });
6244 };
6245
6246 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6247     
6248     cls: false,
6249     align: false,
6250     bgcolor: false,
6251     border: false,
6252     cellpadding: false,
6253     cellspacing: false,
6254     frame: false,
6255     rules: false,
6256     sortable: false,
6257     summary: false,
6258     width: false,
6259     striped : false,
6260     scrollBody : false,
6261     bordered: false,
6262     hover:  false,
6263     condensed : false,
6264     responsive : false,
6265     sm : false,
6266     cm : false,
6267     store : false,
6268     loadMask : false,
6269     footerShow : true,
6270     headerShow : true,
6271   
6272     rowSelection : false,
6273     cellSelection : false,
6274     layout : false,
6275     
6276     // Roo.Element - the tbody
6277     mainBody: false,
6278     // Roo.Element - thead element
6279     mainHead: false,
6280     
6281     container: false, // used by gridpanel...
6282     
6283     lazyLoad : false,
6284     
6285     CSS : Roo.util.CSS,
6286     
6287     auto_hide_footer : false,
6288     
6289     getAutoCreate : function()
6290     {
6291         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6292         
6293         cfg = {
6294             tag: 'table',
6295             cls : 'table',
6296             cn : []
6297         };
6298         if (this.scrollBody) {
6299             cfg.cls += ' table-body-fixed';
6300         }    
6301         if (this.striped) {
6302             cfg.cls += ' table-striped';
6303         }
6304         
6305         if (this.hover) {
6306             cfg.cls += ' table-hover';
6307         }
6308         if (this.bordered) {
6309             cfg.cls += ' table-bordered';
6310         }
6311         if (this.condensed) {
6312             cfg.cls += ' table-condensed';
6313         }
6314         if (this.responsive) {
6315             cfg.cls += ' table-responsive';
6316         }
6317         
6318         if (this.cls) {
6319             cfg.cls+=  ' ' +this.cls;
6320         }
6321         
6322         // this lot should be simplifed...
6323         var _t = this;
6324         var cp = [
6325             'align',
6326             'bgcolor',
6327             'border',
6328             'cellpadding',
6329             'cellspacing',
6330             'frame',
6331             'rules',
6332             'sortable',
6333             'summary',
6334             'width'
6335         ].forEach(function(k) {
6336             if (_t[k]) {
6337                 cfg[k] = _t[k];
6338             }
6339         });
6340         
6341         
6342         if (this.layout) {
6343             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6344         }
6345         
6346         if(this.store || this.cm){
6347             if(this.headerShow){
6348                 cfg.cn.push(this.renderHeader());
6349             }
6350             
6351             cfg.cn.push(this.renderBody());
6352             
6353             if(this.footerShow){
6354                 cfg.cn.push(this.renderFooter());
6355             }
6356             // where does this come from?
6357             //cfg.cls+=  ' TableGrid';
6358         }
6359         
6360         return { cn : [ cfg ] };
6361     },
6362     
6363     initEvents : function()
6364     {   
6365         if(!this.store || !this.cm){
6366             return;
6367         }
6368         if (this.selModel) {
6369             this.selModel.initEvents();
6370         }
6371         
6372         
6373         //Roo.log('initEvents with ds!!!!');
6374         
6375         this.mainBody = this.el.select('tbody', true).first();
6376         this.mainHead = this.el.select('thead', true).first();
6377         this.mainFoot = this.el.select('tfoot', true).first();
6378         
6379         
6380         
6381         var _this = this;
6382         
6383         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6384             e.on('click', _this.sort, _this);
6385         });
6386         
6387         this.mainBody.on("click", this.onClick, this);
6388         this.mainBody.on("dblclick", this.onDblClick, this);
6389         
6390         // why is this done????? = it breaks dialogs??
6391         //this.parent().el.setStyle('position', 'relative');
6392         
6393         
6394         if (this.footer) {
6395             this.footer.parentId = this.id;
6396             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6397             
6398             if(this.lazyLoad){
6399                 this.el.select('tfoot tr td').first().addClass('hide');
6400             }
6401         } 
6402         
6403         if(this.loadMask) {
6404             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6405         }
6406         
6407         this.store.on('load', this.onLoad, this);
6408         this.store.on('beforeload', this.onBeforeLoad, this);
6409         this.store.on('update', this.onUpdate, this);
6410         this.store.on('add', this.onAdd, this);
6411         this.store.on("clear", this.clear, this);
6412         
6413         this.el.on("contextmenu", this.onContextMenu, this);
6414         
6415         this.mainBody.on('scroll', this.onBodyScroll, this);
6416         
6417         this.cm.on("headerchange", this.onHeaderChange, this);
6418         
6419         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6420         
6421     },
6422     
6423     onContextMenu : function(e, t)
6424     {
6425         this.processEvent("contextmenu", e);
6426     },
6427     
6428     processEvent : function(name, e)
6429     {
6430         if (name != 'touchstart' ) {
6431             this.fireEvent(name, e);    
6432         }
6433         
6434         var t = e.getTarget();
6435         
6436         var cell = Roo.get(t);
6437         
6438         if(!cell){
6439             return;
6440         }
6441         
6442         if(cell.findParent('tfoot', false, true)){
6443             return;
6444         }
6445         
6446         if(cell.findParent('thead', false, true)){
6447             
6448             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6449                 cell = Roo.get(t).findParent('th', false, true);
6450                 if (!cell) {
6451                     Roo.log("failed to find th in thead?");
6452                     Roo.log(e.getTarget());
6453                     return;
6454                 }
6455             }
6456             
6457             var cellIndex = cell.dom.cellIndex;
6458             
6459             var ename = name == 'touchstart' ? 'click' : name;
6460             this.fireEvent("header" + ename, this, cellIndex, e);
6461             
6462             return;
6463         }
6464         
6465         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6466             cell = Roo.get(t).findParent('td', false, true);
6467             if (!cell) {
6468                 Roo.log("failed to find th in tbody?");
6469                 Roo.log(e.getTarget());
6470                 return;
6471             }
6472         }
6473         
6474         var row = cell.findParent('tr', false, true);
6475         var cellIndex = cell.dom.cellIndex;
6476         var rowIndex = row.dom.rowIndex - 1;
6477         
6478         if(row !== false){
6479             
6480             this.fireEvent("row" + name, this, rowIndex, e);
6481             
6482             if(cell !== false){
6483             
6484                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6485             }
6486         }
6487         
6488     },
6489     
6490     onMouseover : function(e, el)
6491     {
6492         var cell = Roo.get(el);
6493         
6494         if(!cell){
6495             return;
6496         }
6497         
6498         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6499             cell = cell.findParent('td', false, true);
6500         }
6501         
6502         var row = cell.findParent('tr', false, true);
6503         var cellIndex = cell.dom.cellIndex;
6504         var rowIndex = row.dom.rowIndex - 1; // start from 0
6505         
6506         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6507         
6508     },
6509     
6510     onMouseout : function(e, el)
6511     {
6512         var cell = Roo.get(el);
6513         
6514         if(!cell){
6515             return;
6516         }
6517         
6518         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6519             cell = cell.findParent('td', false, true);
6520         }
6521         
6522         var row = cell.findParent('tr', false, true);
6523         var cellIndex = cell.dom.cellIndex;
6524         var rowIndex = row.dom.rowIndex - 1; // start from 0
6525         
6526         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6527         
6528     },
6529     
6530     onClick : function(e, el)
6531     {
6532         var cell = Roo.get(el);
6533         
6534         if(!cell || (!this.cellSelection && !this.rowSelection)){
6535             return;
6536         }
6537         
6538         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6539             cell = cell.findParent('td', false, true);
6540         }
6541         
6542         if(!cell || typeof(cell) == 'undefined'){
6543             return;
6544         }
6545         
6546         var row = cell.findParent('tr', false, true);
6547         
6548         if(!row || typeof(row) == 'undefined'){
6549             return;
6550         }
6551         
6552         var cellIndex = cell.dom.cellIndex;
6553         var rowIndex = this.getRowIndex(row);
6554         
6555         // why??? - should these not be based on SelectionModel?
6556         if(this.cellSelection){
6557             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6558         }
6559         
6560         if(this.rowSelection){
6561             this.fireEvent('rowclick', this, row, rowIndex, e);
6562         }
6563         
6564         
6565     },
6566         
6567     onDblClick : function(e,el)
6568     {
6569         var cell = Roo.get(el);
6570         
6571         if(!cell || (!this.cellSelection && !this.rowSelection)){
6572             return;
6573         }
6574         
6575         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6576             cell = cell.findParent('td', false, true);
6577         }
6578         
6579         if(!cell || typeof(cell) == 'undefined'){
6580             return;
6581         }
6582         
6583         var row = cell.findParent('tr', false, true);
6584         
6585         if(!row || typeof(row) == 'undefined'){
6586             return;
6587         }
6588         
6589         var cellIndex = cell.dom.cellIndex;
6590         var rowIndex = this.getRowIndex(row);
6591         
6592         if(this.cellSelection){
6593             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6594         }
6595         
6596         if(this.rowSelection){
6597             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6598         }
6599     },
6600     
6601     sort : function(e,el)
6602     {
6603         var col = Roo.get(el);
6604         
6605         if(!col.hasClass('sortable')){
6606             return;
6607         }
6608         
6609         var sort = col.attr('sort');
6610         var dir = 'ASC';
6611         
6612         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6613             dir = 'DESC';
6614         }
6615         
6616         this.store.sortInfo = {field : sort, direction : dir};
6617         
6618         if (this.footer) {
6619             Roo.log("calling footer first");
6620             this.footer.onClick('first');
6621         } else {
6622         
6623             this.store.load({ params : { start : 0 } });
6624         }
6625     },
6626     
6627     renderHeader : function()
6628     {
6629         var header = {
6630             tag: 'thead',
6631             cn : []
6632         };
6633         
6634         var cm = this.cm;
6635         this.totalWidth = 0;
6636         
6637         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6638             
6639             var config = cm.config[i];
6640             
6641             var c = {
6642                 tag: 'th',
6643                 cls : 'x-hcol-' + i,
6644                 style : '',
6645                 html: cm.getColumnHeader(i)
6646             };
6647             
6648             var hh = '';
6649             
6650             if(typeof(config.sortable) != 'undefined' && config.sortable){
6651                 c.cls = 'sortable';
6652                 c.html = '<i class="glyphicon"></i>' + c.html;
6653             }
6654             
6655             if(typeof(config.lgHeader) != 'undefined'){
6656                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6657             }
6658             
6659             if(typeof(config.mdHeader) != 'undefined'){
6660                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6661             }
6662             
6663             if(typeof(config.smHeader) != 'undefined'){
6664                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6665             }
6666             
6667             if(typeof(config.xsHeader) != 'undefined'){
6668                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6669             }
6670             
6671             if(hh.length){
6672                 c.html = hh;
6673             }
6674             
6675             if(typeof(config.tooltip) != 'undefined'){
6676                 c.tooltip = config.tooltip;
6677             }
6678             
6679             if(typeof(config.colspan) != 'undefined'){
6680                 c.colspan = config.colspan;
6681             }
6682             
6683             if(typeof(config.hidden) != 'undefined' && config.hidden){
6684                 c.style += ' display:none;';
6685             }
6686             
6687             if(typeof(config.dataIndex) != 'undefined'){
6688                 c.sort = config.dataIndex;
6689             }
6690             
6691            
6692             
6693             if(typeof(config.align) != 'undefined' && config.align.length){
6694                 c.style += ' text-align:' + config.align + ';';
6695             }
6696             
6697             if(typeof(config.width) != 'undefined'){
6698                 c.style += ' width:' + config.width + 'px;';
6699                 this.totalWidth += config.width;
6700             } else {
6701                 this.totalWidth += 100; // assume minimum of 100 per column?
6702             }
6703             
6704             if(typeof(config.cls) != 'undefined'){
6705                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6706             }
6707             
6708             ['xs','sm','md','lg'].map(function(size){
6709                 
6710                 if(typeof(config[size]) == 'undefined'){
6711                     return;
6712                 }
6713                 
6714                 if (!config[size]) { // 0 = hidden
6715                     c.cls += ' hidden-' + size;
6716                     return;
6717                 }
6718                 
6719                 c.cls += ' col-' + size + '-' + config[size];
6720
6721             });
6722             
6723             header.cn.push(c)
6724         }
6725         
6726         return header;
6727     },
6728     
6729     renderBody : function()
6730     {
6731         var body = {
6732             tag: 'tbody',
6733             cn : [
6734                 {
6735                     tag: 'tr',
6736                     cn : [
6737                         {
6738                             tag : 'td',
6739                             colspan :  this.cm.getColumnCount()
6740                         }
6741                     ]
6742                 }
6743             ]
6744         };
6745         
6746         return body;
6747     },
6748     
6749     renderFooter : function()
6750     {
6751         var footer = {
6752             tag: 'tfoot',
6753             cn : [
6754                 {
6755                     tag: 'tr',
6756                     cn : [
6757                         {
6758                             tag : 'td',
6759                             colspan :  this.cm.getColumnCount()
6760                         }
6761                     ]
6762                 }
6763             ]
6764         };
6765         
6766         return footer;
6767     },
6768     
6769     
6770     
6771     onLoad : function()
6772     {
6773 //        Roo.log('ds onload');
6774         this.clear();
6775         
6776         var _this = this;
6777         var cm = this.cm;
6778         var ds = this.store;
6779         
6780         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6781             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6782             if (_this.store.sortInfo) {
6783                     
6784                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6785                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6786                 }
6787                 
6788                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6789                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6790                 }
6791             }
6792         });
6793         
6794         var tbody =  this.mainBody;
6795               
6796         if(ds.getCount() > 0){
6797             ds.data.each(function(d,rowIndex){
6798                 var row =  this.renderRow(cm, ds, rowIndex);
6799                 
6800                 tbody.createChild(row);
6801                 
6802                 var _this = this;
6803                 
6804                 if(row.cellObjects.length){
6805                     Roo.each(row.cellObjects, function(r){
6806                         _this.renderCellObject(r);
6807                     })
6808                 }
6809                 
6810             }, this);
6811         }
6812         
6813         var tfoot = this.el.select('tfoot', true).first();
6814         
6815         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6816             
6817             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6818             
6819             var total = this.ds.getTotalCount();
6820             
6821             if(this.footer.pageSize < total){
6822                 this.mainFoot.show();
6823             }
6824         }
6825         
6826         Roo.each(this.el.select('tbody td', true).elements, function(e){
6827             e.on('mouseover', _this.onMouseover, _this);
6828         });
6829         
6830         Roo.each(this.el.select('tbody td', true).elements, function(e){
6831             e.on('mouseout', _this.onMouseout, _this);
6832         });
6833         this.fireEvent('rowsrendered', this);
6834         
6835         this.autoSize();
6836     },
6837     
6838     
6839     onUpdate : function(ds,record)
6840     {
6841         this.refreshRow(record);
6842         this.autoSize();
6843     },
6844     
6845     onRemove : function(ds, record, index, isUpdate){
6846         if(isUpdate !== true){
6847             this.fireEvent("beforerowremoved", this, index, record);
6848         }
6849         var bt = this.mainBody.dom;
6850         
6851         var rows = this.el.select('tbody > tr', true).elements;
6852         
6853         if(typeof(rows[index]) != 'undefined'){
6854             bt.removeChild(rows[index].dom);
6855         }
6856         
6857 //        if(bt.rows[index]){
6858 //            bt.removeChild(bt.rows[index]);
6859 //        }
6860         
6861         if(isUpdate !== true){
6862             //this.stripeRows(index);
6863             //this.syncRowHeights(index, index);
6864             //this.layout();
6865             this.fireEvent("rowremoved", this, index, record);
6866         }
6867     },
6868     
6869     onAdd : function(ds, records, rowIndex)
6870     {
6871         //Roo.log('on Add called');
6872         // - note this does not handle multiple adding very well..
6873         var bt = this.mainBody.dom;
6874         for (var i =0 ; i < records.length;i++) {
6875             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6876             //Roo.log(records[i]);
6877             //Roo.log(this.store.getAt(rowIndex+i));
6878             this.insertRow(this.store, rowIndex + i, false);
6879             return;
6880         }
6881         
6882     },
6883     
6884     
6885     refreshRow : function(record){
6886         var ds = this.store, index;
6887         if(typeof record == 'number'){
6888             index = record;
6889             record = ds.getAt(index);
6890         }else{
6891             index = ds.indexOf(record);
6892         }
6893         this.insertRow(ds, index, true);
6894         this.autoSize();
6895         this.onRemove(ds, record, index+1, true);
6896         this.autoSize();
6897         //this.syncRowHeights(index, index);
6898         //this.layout();
6899         this.fireEvent("rowupdated", this, index, record);
6900     },
6901     
6902     insertRow : function(dm, rowIndex, isUpdate){
6903         
6904         if(!isUpdate){
6905             this.fireEvent("beforerowsinserted", this, rowIndex);
6906         }
6907             //var s = this.getScrollState();
6908         var row = this.renderRow(this.cm, this.store, rowIndex);
6909         // insert before rowIndex..
6910         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6911         
6912         var _this = this;
6913                 
6914         if(row.cellObjects.length){
6915             Roo.each(row.cellObjects, function(r){
6916                 _this.renderCellObject(r);
6917             })
6918         }
6919             
6920         if(!isUpdate){
6921             this.fireEvent("rowsinserted", this, rowIndex);
6922             //this.syncRowHeights(firstRow, lastRow);
6923             //this.stripeRows(firstRow);
6924             //this.layout();
6925         }
6926         
6927     },
6928     
6929     
6930     getRowDom : function(rowIndex)
6931     {
6932         var rows = this.el.select('tbody > tr', true).elements;
6933         
6934         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6935         
6936     },
6937     // returns the object tree for a tr..
6938   
6939     
6940     renderRow : function(cm, ds, rowIndex) 
6941     {
6942         var d = ds.getAt(rowIndex);
6943         
6944         var row = {
6945             tag : 'tr',
6946             cls : 'x-row-' + rowIndex,
6947             cn : []
6948         };
6949             
6950         var cellObjects = [];
6951         
6952         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6953             var config = cm.config[i];
6954             
6955             var renderer = cm.getRenderer(i);
6956             var value = '';
6957             var id = false;
6958             
6959             if(typeof(renderer) !== 'undefined'){
6960                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6961             }
6962             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6963             // and are rendered into the cells after the row is rendered - using the id for the element.
6964             
6965             if(typeof(value) === 'object'){
6966                 id = Roo.id();
6967                 cellObjects.push({
6968                     container : id,
6969                     cfg : value 
6970                 })
6971             }
6972             
6973             var rowcfg = {
6974                 record: d,
6975                 rowIndex : rowIndex,
6976                 colIndex : i,
6977                 rowClass : ''
6978             };
6979
6980             this.fireEvent('rowclass', this, rowcfg);
6981             
6982             var td = {
6983                 tag: 'td',
6984                 cls : rowcfg.rowClass + ' x-col-' + i,
6985                 style: '',
6986                 html: (typeof(value) === 'object') ? '' : value
6987             };
6988             
6989             if (id) {
6990                 td.id = id;
6991             }
6992             
6993             if(typeof(config.colspan) != 'undefined'){
6994                 td.colspan = config.colspan;
6995             }
6996             
6997             if(typeof(config.hidden) != 'undefined' && config.hidden){
6998                 td.style += ' display:none;';
6999             }
7000             
7001             if(typeof(config.align) != 'undefined' && config.align.length){
7002                 td.style += ' text-align:' + config.align + ';';
7003             }
7004             if(typeof(config.valign) != 'undefined' && config.valign.length){
7005                 td.style += ' vertical-align:' + config.valign + ';';
7006             }
7007             
7008             if(typeof(config.width) != 'undefined'){
7009                 td.style += ' width:' +  config.width + 'px;';
7010             }
7011             
7012             if(typeof(config.cursor) != 'undefined'){
7013                 td.style += ' cursor:' +  config.cursor + ';';
7014             }
7015             
7016             if(typeof(config.cls) != 'undefined'){
7017                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7018             }
7019             
7020             ['xs','sm','md','lg'].map(function(size){
7021                 
7022                 if(typeof(config[size]) == 'undefined'){
7023                     return;
7024                 }
7025                 
7026                 if (!config[size]) { // 0 = hidden
7027                     td.cls += ' hidden-' + size;
7028                     return;
7029                 }
7030                 
7031                 td.cls += ' col-' + size + '-' + config[size];
7032
7033             });
7034             
7035             row.cn.push(td);
7036            
7037         }
7038         
7039         row.cellObjects = cellObjects;
7040         
7041         return row;
7042           
7043     },
7044     
7045     
7046     
7047     onBeforeLoad : function()
7048     {
7049         
7050     },
7051      /**
7052      * Remove all rows
7053      */
7054     clear : function()
7055     {
7056         this.el.select('tbody', true).first().dom.innerHTML = '';
7057     },
7058     /**
7059      * Show or hide a row.
7060      * @param {Number} rowIndex to show or hide
7061      * @param {Boolean} state hide
7062      */
7063     setRowVisibility : function(rowIndex, state)
7064     {
7065         var bt = this.mainBody.dom;
7066         
7067         var rows = this.el.select('tbody > tr', true).elements;
7068         
7069         if(typeof(rows[rowIndex]) == 'undefined'){
7070             return;
7071         }
7072         rows[rowIndex].dom.style.display = state ? '' : 'none';
7073     },
7074     
7075     
7076     getSelectionModel : function(){
7077         if(!this.selModel){
7078             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7079         }
7080         return this.selModel;
7081     },
7082     /*
7083      * Render the Roo.bootstrap object from renderder
7084      */
7085     renderCellObject : function(r)
7086     {
7087         var _this = this;
7088         
7089         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7090         
7091         var t = r.cfg.render(r.container);
7092         
7093         if(r.cfg.cn){
7094             Roo.each(r.cfg.cn, function(c){
7095                 var child = {
7096                     container: t.getChildContainer(),
7097                     cfg: c
7098                 };
7099                 _this.renderCellObject(child);
7100             })
7101         }
7102     },
7103     
7104     getRowIndex : function(row)
7105     {
7106         var rowIndex = -1;
7107         
7108         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7109             if(el != row){
7110                 return;
7111             }
7112             
7113             rowIndex = index;
7114         });
7115         
7116         return rowIndex;
7117     },
7118      /**
7119      * Returns the grid's underlying element = used by panel.Grid
7120      * @return {Element} The element
7121      */
7122     getGridEl : function(){
7123         return this.el;
7124     },
7125      /**
7126      * Forces a resize - used by panel.Grid
7127      * @return {Element} The element
7128      */
7129     autoSize : function()
7130     {
7131         //var ctr = Roo.get(this.container.dom.parentElement);
7132         var ctr = Roo.get(this.el.dom);
7133         
7134         var thd = this.getGridEl().select('thead',true).first();
7135         var tbd = this.getGridEl().select('tbody', true).first();
7136         var tfd = this.getGridEl().select('tfoot', true).first();
7137         
7138         var cw = ctr.getWidth();
7139         
7140         if (tbd) {
7141             
7142             tbd.setSize(ctr.getWidth(),
7143                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7144             );
7145             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7146             cw -= barsize;
7147         }
7148         cw = Math.max(cw, this.totalWidth);
7149         this.getGridEl().select('tr',true).setWidth(cw);
7150         // resize 'expandable coloumn?
7151         
7152         return; // we doe not have a view in this design..
7153         
7154     },
7155     onBodyScroll: function()
7156     {
7157         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7158         if(this.mainHead){
7159             this.mainHead.setStyle({
7160                 'position' : 'relative',
7161                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7162             });
7163         }
7164         
7165         if(this.lazyLoad){
7166             
7167             var scrollHeight = this.mainBody.dom.scrollHeight;
7168             
7169             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7170             
7171             var height = this.mainBody.getHeight();
7172             
7173             if(scrollHeight - height == scrollTop) {
7174                 
7175                 var total = this.ds.getTotalCount();
7176                 
7177                 if(this.footer.cursor + this.footer.pageSize < total){
7178                     
7179                     this.footer.ds.load({
7180                         params : {
7181                             start : this.footer.cursor + this.footer.pageSize,
7182                             limit : this.footer.pageSize
7183                         },
7184                         add : true
7185                     });
7186                 }
7187             }
7188             
7189         }
7190     },
7191     
7192     onHeaderChange : function()
7193     {
7194         var header = this.renderHeader();
7195         var table = this.el.select('table', true).first();
7196         
7197         this.mainHead.remove();
7198         this.mainHead = table.createChild(header, this.mainBody, false);
7199     },
7200     
7201     onHiddenChange : function(colModel, colIndex, hidden)
7202     {
7203         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7204         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7205         
7206         this.CSS.updateRule(thSelector, "display", "");
7207         this.CSS.updateRule(tdSelector, "display", "");
7208         
7209         if(hidden){
7210             this.CSS.updateRule(thSelector, "display", "none");
7211             this.CSS.updateRule(tdSelector, "display", "none");
7212         }
7213         
7214         this.onHeaderChange();
7215         this.onLoad();
7216     },
7217     
7218     setColumnWidth: function(col_index, width)
7219     {
7220         // width = "md-2 xs-2..."
7221         if(!this.colModel.config[col_index]) {
7222             return;
7223         }
7224         
7225         var w = width.split(" ");
7226         
7227         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7228         
7229         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7230         
7231         
7232         for(var j = 0; j < w.length; j++) {
7233             
7234             if(!w[j]) {
7235                 continue;
7236             }
7237             
7238             var size_cls = w[j].split("-");
7239             
7240             if(!Number.isInteger(size_cls[1] * 1)) {
7241                 continue;
7242             }
7243             
7244             if(!this.colModel.config[col_index][size_cls[0]]) {
7245                 continue;
7246             }
7247             
7248             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7249                 continue;
7250             }
7251             
7252             h_row[0].classList.replace(
7253                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7254                 "col-"+size_cls[0]+"-"+size_cls[1]
7255             );
7256             
7257             for(var i = 0; i < rows.length; i++) {
7258                 
7259                 var size_cls = w[j].split("-");
7260                 
7261                 if(!Number.isInteger(size_cls[1] * 1)) {
7262                     continue;
7263                 }
7264                 
7265                 if(!this.colModel.config[col_index][size_cls[0]]) {
7266                     continue;
7267                 }
7268                 
7269                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7270                     continue;
7271                 }
7272                 
7273                 rows[i].classList.replace(
7274                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7275                     "col-"+size_cls[0]+"-"+size_cls[1]
7276                 );
7277             }
7278             
7279             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7280         }
7281     }
7282 });
7283
7284  
7285
7286  /*
7287  * - LGPL
7288  *
7289  * table cell
7290  * 
7291  */
7292
7293 /**
7294  * @class Roo.bootstrap.TableCell
7295  * @extends Roo.bootstrap.Component
7296  * Bootstrap TableCell class
7297  * @cfg {String} html cell contain text
7298  * @cfg {String} cls cell class
7299  * @cfg {String} tag cell tag (td|th) default td
7300  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7301  * @cfg {String} align Aligns the content in a cell
7302  * @cfg {String} axis Categorizes cells
7303  * @cfg {String} bgcolor Specifies the background color of a cell
7304  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7305  * @cfg {Number} colspan Specifies the number of columns a cell should span
7306  * @cfg {String} headers Specifies one or more header cells a cell is related to
7307  * @cfg {Number} height Sets the height of a cell
7308  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7309  * @cfg {Number} rowspan Sets the number of rows a cell should span
7310  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7311  * @cfg {String} valign Vertical aligns the content in a cell
7312  * @cfg {Number} width Specifies the width of a cell
7313  * 
7314  * @constructor
7315  * Create a new TableCell
7316  * @param {Object} config The config object
7317  */
7318
7319 Roo.bootstrap.TableCell = function(config){
7320     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7321 };
7322
7323 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7324     
7325     html: false,
7326     cls: false,
7327     tag: false,
7328     abbr: false,
7329     align: false,
7330     axis: false,
7331     bgcolor: false,
7332     charoff: false,
7333     colspan: false,
7334     headers: false,
7335     height: false,
7336     nowrap: false,
7337     rowspan: false,
7338     scope: false,
7339     valign: false,
7340     width: false,
7341     
7342     
7343     getAutoCreate : function(){
7344         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7345         
7346         cfg = {
7347             tag: 'td'
7348         };
7349         
7350         if(this.tag){
7351             cfg.tag = this.tag;
7352         }
7353         
7354         if (this.html) {
7355             cfg.html=this.html
7356         }
7357         if (this.cls) {
7358             cfg.cls=this.cls
7359         }
7360         if (this.abbr) {
7361             cfg.abbr=this.abbr
7362         }
7363         if (this.align) {
7364             cfg.align=this.align
7365         }
7366         if (this.axis) {
7367             cfg.axis=this.axis
7368         }
7369         if (this.bgcolor) {
7370             cfg.bgcolor=this.bgcolor
7371         }
7372         if (this.charoff) {
7373             cfg.charoff=this.charoff
7374         }
7375         if (this.colspan) {
7376             cfg.colspan=this.colspan
7377         }
7378         if (this.headers) {
7379             cfg.headers=this.headers
7380         }
7381         if (this.height) {
7382             cfg.height=this.height
7383         }
7384         if (this.nowrap) {
7385             cfg.nowrap=this.nowrap
7386         }
7387         if (this.rowspan) {
7388             cfg.rowspan=this.rowspan
7389         }
7390         if (this.scope) {
7391             cfg.scope=this.scope
7392         }
7393         if (this.valign) {
7394             cfg.valign=this.valign
7395         }
7396         if (this.width) {
7397             cfg.width=this.width
7398         }
7399         
7400         
7401         return cfg;
7402     }
7403    
7404 });
7405
7406  
7407
7408  /*
7409  * - LGPL
7410  *
7411  * table row
7412  * 
7413  */
7414
7415 /**
7416  * @class Roo.bootstrap.TableRow
7417  * @extends Roo.bootstrap.Component
7418  * Bootstrap TableRow class
7419  * @cfg {String} cls row class
7420  * @cfg {String} align Aligns the content in a table row
7421  * @cfg {String} bgcolor Specifies a background color for a table row
7422  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7423  * @cfg {String} valign Vertical aligns the content in a table row
7424  * 
7425  * @constructor
7426  * Create a new TableRow
7427  * @param {Object} config The config object
7428  */
7429
7430 Roo.bootstrap.TableRow = function(config){
7431     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7432 };
7433
7434 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7435     
7436     cls: false,
7437     align: false,
7438     bgcolor: false,
7439     charoff: false,
7440     valign: false,
7441     
7442     getAutoCreate : function(){
7443         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7444         
7445         cfg = {
7446             tag: 'tr'
7447         };
7448             
7449         if(this.cls){
7450             cfg.cls = this.cls;
7451         }
7452         if(this.align){
7453             cfg.align = this.align;
7454         }
7455         if(this.bgcolor){
7456             cfg.bgcolor = this.bgcolor;
7457         }
7458         if(this.charoff){
7459             cfg.charoff = this.charoff;
7460         }
7461         if(this.valign){
7462             cfg.valign = this.valign;
7463         }
7464         
7465         return cfg;
7466     }
7467    
7468 });
7469
7470  
7471
7472  /*
7473  * - LGPL
7474  *
7475  * table body
7476  * 
7477  */
7478
7479 /**
7480  * @class Roo.bootstrap.TableBody
7481  * @extends Roo.bootstrap.Component
7482  * Bootstrap TableBody class
7483  * @cfg {String} cls element class
7484  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7485  * @cfg {String} align Aligns the content inside the element
7486  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7487  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7488  * 
7489  * @constructor
7490  * Create a new TableBody
7491  * @param {Object} config The config object
7492  */
7493
7494 Roo.bootstrap.TableBody = function(config){
7495     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7496 };
7497
7498 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7499     
7500     cls: false,
7501     tag: false,
7502     align: false,
7503     charoff: false,
7504     valign: false,
7505     
7506     getAutoCreate : function(){
7507         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7508         
7509         cfg = {
7510             tag: 'tbody'
7511         };
7512             
7513         if (this.cls) {
7514             cfg.cls=this.cls
7515         }
7516         if(this.tag){
7517             cfg.tag = this.tag;
7518         }
7519         
7520         if(this.align){
7521             cfg.align = this.align;
7522         }
7523         if(this.charoff){
7524             cfg.charoff = this.charoff;
7525         }
7526         if(this.valign){
7527             cfg.valign = this.valign;
7528         }
7529         
7530         return cfg;
7531     }
7532     
7533     
7534 //    initEvents : function()
7535 //    {
7536 //        
7537 //        if(!this.store){
7538 //            return;
7539 //        }
7540 //        
7541 //        this.store = Roo.factory(this.store, Roo.data);
7542 //        this.store.on('load', this.onLoad, this);
7543 //        
7544 //        this.store.load();
7545 //        
7546 //    },
7547 //    
7548 //    onLoad: function () 
7549 //    {   
7550 //        this.fireEvent('load', this);
7551 //    }
7552 //    
7553 //   
7554 });
7555
7556  
7557
7558  /*
7559  * Based on:
7560  * Ext JS Library 1.1.1
7561  * Copyright(c) 2006-2007, Ext JS, LLC.
7562  *
7563  * Originally Released Under LGPL - original licence link has changed is not relivant.
7564  *
7565  * Fork - LGPL
7566  * <script type="text/javascript">
7567  */
7568
7569 // as we use this in bootstrap.
7570 Roo.namespace('Roo.form');
7571  /**
7572  * @class Roo.form.Action
7573  * Internal Class used to handle form actions
7574  * @constructor
7575  * @param {Roo.form.BasicForm} el The form element or its id
7576  * @param {Object} config Configuration options
7577  */
7578
7579  
7580  
7581 // define the action interface
7582 Roo.form.Action = function(form, options){
7583     this.form = form;
7584     this.options = options || {};
7585 };
7586 /**
7587  * Client Validation Failed
7588  * @const 
7589  */
7590 Roo.form.Action.CLIENT_INVALID = 'client';
7591 /**
7592  * Server Validation Failed
7593  * @const 
7594  */
7595 Roo.form.Action.SERVER_INVALID = 'server';
7596  /**
7597  * Connect to Server Failed
7598  * @const 
7599  */
7600 Roo.form.Action.CONNECT_FAILURE = 'connect';
7601 /**
7602  * Reading Data from Server Failed
7603  * @const 
7604  */
7605 Roo.form.Action.LOAD_FAILURE = 'load';
7606
7607 Roo.form.Action.prototype = {
7608     type : 'default',
7609     failureType : undefined,
7610     response : undefined,
7611     result : undefined,
7612
7613     // interface method
7614     run : function(options){
7615
7616     },
7617
7618     // interface method
7619     success : function(response){
7620
7621     },
7622
7623     // interface method
7624     handleResponse : function(response){
7625
7626     },
7627
7628     // default connection failure
7629     failure : function(response){
7630         
7631         this.response = response;
7632         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7633         this.form.afterAction(this, false);
7634     },
7635
7636     processResponse : function(response){
7637         this.response = response;
7638         if(!response.responseText){
7639             return true;
7640         }
7641         this.result = this.handleResponse(response);
7642         return this.result;
7643     },
7644
7645     // utility functions used internally
7646     getUrl : function(appendParams){
7647         var url = this.options.url || this.form.url || this.form.el.dom.action;
7648         if(appendParams){
7649             var p = this.getParams();
7650             if(p){
7651                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7652             }
7653         }
7654         return url;
7655     },
7656
7657     getMethod : function(){
7658         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7659     },
7660
7661     getParams : function(){
7662         var bp = this.form.baseParams;
7663         var p = this.options.params;
7664         if(p){
7665             if(typeof p == "object"){
7666                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7667             }else if(typeof p == 'string' && bp){
7668                 p += '&' + Roo.urlEncode(bp);
7669             }
7670         }else if(bp){
7671             p = Roo.urlEncode(bp);
7672         }
7673         return p;
7674     },
7675
7676     createCallback : function(){
7677         return {
7678             success: this.success,
7679             failure: this.failure,
7680             scope: this,
7681             timeout: (this.form.timeout*1000),
7682             upload: this.form.fileUpload ? this.success : undefined
7683         };
7684     }
7685 };
7686
7687 Roo.form.Action.Submit = function(form, options){
7688     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7689 };
7690
7691 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7692     type : 'submit',
7693
7694     haveProgress : false,
7695     uploadComplete : false,
7696     
7697     // uploadProgress indicator.
7698     uploadProgress : function()
7699     {
7700         if (!this.form.progressUrl) {
7701             return;
7702         }
7703         
7704         if (!this.haveProgress) {
7705             Roo.MessageBox.progress("Uploading", "Uploading");
7706         }
7707         if (this.uploadComplete) {
7708            Roo.MessageBox.hide();
7709            return;
7710         }
7711         
7712         this.haveProgress = true;
7713    
7714         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7715         
7716         var c = new Roo.data.Connection();
7717         c.request({
7718             url : this.form.progressUrl,
7719             params: {
7720                 id : uid
7721             },
7722             method: 'GET',
7723             success : function(req){
7724                //console.log(data);
7725                 var rdata = false;
7726                 var edata;
7727                 try  {
7728                    rdata = Roo.decode(req.responseText)
7729                 } catch (e) {
7730                     Roo.log("Invalid data from server..");
7731                     Roo.log(edata);
7732                     return;
7733                 }
7734                 if (!rdata || !rdata.success) {
7735                     Roo.log(rdata);
7736                     Roo.MessageBox.alert(Roo.encode(rdata));
7737                     return;
7738                 }
7739                 var data = rdata.data;
7740                 
7741                 if (this.uploadComplete) {
7742                    Roo.MessageBox.hide();
7743                    return;
7744                 }
7745                    
7746                 if (data){
7747                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7748                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7749                     );
7750                 }
7751                 this.uploadProgress.defer(2000,this);
7752             },
7753        
7754             failure: function(data) {
7755                 Roo.log('progress url failed ');
7756                 Roo.log(data);
7757             },
7758             scope : this
7759         });
7760            
7761     },
7762     
7763     
7764     run : function()
7765     {
7766         // run get Values on the form, so it syncs any secondary forms.
7767         this.form.getValues();
7768         
7769         var o = this.options;
7770         var method = this.getMethod();
7771         var isPost = method == 'POST';
7772         if(o.clientValidation === false || this.form.isValid()){
7773             
7774             if (this.form.progressUrl) {
7775                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7776                     (new Date() * 1) + '' + Math.random());
7777                     
7778             } 
7779             
7780             
7781             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7782                 form:this.form.el.dom,
7783                 url:this.getUrl(!isPost),
7784                 method: method,
7785                 params:isPost ? this.getParams() : null,
7786                 isUpload: this.form.fileUpload
7787             }));
7788             
7789             this.uploadProgress();
7790
7791         }else if (o.clientValidation !== false){ // client validation failed
7792             this.failureType = Roo.form.Action.CLIENT_INVALID;
7793             this.form.afterAction(this, false);
7794         }
7795     },
7796
7797     success : function(response)
7798     {
7799         this.uploadComplete= true;
7800         if (this.haveProgress) {
7801             Roo.MessageBox.hide();
7802         }
7803         
7804         
7805         var result = this.processResponse(response);
7806         if(result === true || result.success){
7807             this.form.afterAction(this, true);
7808             return;
7809         }
7810         if(result.errors){
7811             this.form.markInvalid(result.errors);
7812             this.failureType = Roo.form.Action.SERVER_INVALID;
7813         }
7814         this.form.afterAction(this, false);
7815     },
7816     failure : function(response)
7817     {
7818         this.uploadComplete= true;
7819         if (this.haveProgress) {
7820             Roo.MessageBox.hide();
7821         }
7822         
7823         this.response = response;
7824         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7825         this.form.afterAction(this, false);
7826     },
7827     
7828     handleResponse : function(response){
7829         if(this.form.errorReader){
7830             var rs = this.form.errorReader.read(response);
7831             var errors = [];
7832             if(rs.records){
7833                 for(var i = 0, len = rs.records.length; i < len; i++) {
7834                     var r = rs.records[i];
7835                     errors[i] = r.data;
7836                 }
7837             }
7838             if(errors.length < 1){
7839                 errors = null;
7840             }
7841             return {
7842                 success : rs.success,
7843                 errors : errors
7844             };
7845         }
7846         var ret = false;
7847         try {
7848             ret = Roo.decode(response.responseText);
7849         } catch (e) {
7850             ret = {
7851                 success: false,
7852                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7853                 errors : []
7854             };
7855         }
7856         return ret;
7857         
7858     }
7859 });
7860
7861
7862 Roo.form.Action.Load = function(form, options){
7863     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7864     this.reader = this.form.reader;
7865 };
7866
7867 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7868     type : 'load',
7869
7870     run : function(){
7871         
7872         Roo.Ajax.request(Roo.apply(
7873                 this.createCallback(), {
7874                     method:this.getMethod(),
7875                     url:this.getUrl(false),
7876                     params:this.getParams()
7877         }));
7878     },
7879
7880     success : function(response){
7881         
7882         var result = this.processResponse(response);
7883         if(result === true || !result.success || !result.data){
7884             this.failureType = Roo.form.Action.LOAD_FAILURE;
7885             this.form.afterAction(this, false);
7886             return;
7887         }
7888         this.form.clearInvalid();
7889         this.form.setValues(result.data);
7890         this.form.afterAction(this, true);
7891     },
7892
7893     handleResponse : function(response){
7894         if(this.form.reader){
7895             var rs = this.form.reader.read(response);
7896             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7897             return {
7898                 success : rs.success,
7899                 data : data
7900             };
7901         }
7902         return Roo.decode(response.responseText);
7903     }
7904 });
7905
7906 Roo.form.Action.ACTION_TYPES = {
7907     'load' : Roo.form.Action.Load,
7908     'submit' : Roo.form.Action.Submit
7909 };/*
7910  * - LGPL
7911  *
7912  * form
7913  *
7914  */
7915
7916 /**
7917  * @class Roo.bootstrap.Form
7918  * @extends Roo.bootstrap.Component
7919  * Bootstrap Form class
7920  * @cfg {String} method  GET | POST (default POST)
7921  * @cfg {String} labelAlign top | left (default top)
7922  * @cfg {String} align left  | right - for navbars
7923  * @cfg {Boolean} loadMask load mask when submit (default true)
7924
7925  *
7926  * @constructor
7927  * Create a new Form
7928  * @param {Object} config The config object
7929  */
7930
7931
7932 Roo.bootstrap.Form = function(config){
7933     
7934     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7935     
7936     Roo.bootstrap.Form.popover.apply();
7937     
7938     this.addEvents({
7939         /**
7940          * @event clientvalidation
7941          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7942          * @param {Form} this
7943          * @param {Boolean} valid true if the form has passed client-side validation
7944          */
7945         clientvalidation: true,
7946         /**
7947          * @event beforeaction
7948          * Fires before any action is performed. Return false to cancel the action.
7949          * @param {Form} this
7950          * @param {Action} action The action to be performed
7951          */
7952         beforeaction: true,
7953         /**
7954          * @event actionfailed
7955          * Fires when an action fails.
7956          * @param {Form} this
7957          * @param {Action} action The action that failed
7958          */
7959         actionfailed : true,
7960         /**
7961          * @event actioncomplete
7962          * Fires when an action is completed.
7963          * @param {Form} this
7964          * @param {Action} action The action that completed
7965          */
7966         actioncomplete : true
7967     });
7968 };
7969
7970 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7971
7972      /**
7973      * @cfg {String} method
7974      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7975      */
7976     method : 'POST',
7977     /**
7978      * @cfg {String} url
7979      * The URL to use for form actions if one isn't supplied in the action options.
7980      */
7981     /**
7982      * @cfg {Boolean} fileUpload
7983      * Set to true if this form is a file upload.
7984      */
7985
7986     /**
7987      * @cfg {Object} baseParams
7988      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7989      */
7990
7991     /**
7992      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7993      */
7994     timeout: 30,
7995     /**
7996      * @cfg {Sting} align (left|right) for navbar forms
7997      */
7998     align : 'left',
7999
8000     // private
8001     activeAction : null,
8002
8003     /**
8004      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8005      * element by passing it or its id or mask the form itself by passing in true.
8006      * @type Mixed
8007      */
8008     waitMsgTarget : false,
8009
8010     loadMask : true,
8011     
8012     /**
8013      * @cfg {Boolean} errorMask (true|false) default false
8014      */
8015     errorMask : false,
8016     
8017     /**
8018      * @cfg {Number} maskOffset Default 100
8019      */
8020     maskOffset : 100,
8021     
8022     /**
8023      * @cfg {Boolean} maskBody
8024      */
8025     maskBody : false,
8026
8027     getAutoCreate : function(){
8028
8029         var cfg = {
8030             tag: 'form',
8031             method : this.method || 'POST',
8032             id : this.id || Roo.id(),
8033             cls : ''
8034         };
8035         if (this.parent().xtype.match(/^Nav/)) {
8036             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8037
8038         }
8039
8040         if (this.labelAlign == 'left' ) {
8041             cfg.cls += ' form-horizontal';
8042         }
8043
8044
8045         return cfg;
8046     },
8047     initEvents : function()
8048     {
8049         this.el.on('submit', this.onSubmit, this);
8050         // this was added as random key presses on the form where triggering form submit.
8051         this.el.on('keypress', function(e) {
8052             if (e.getCharCode() != 13) {
8053                 return true;
8054             }
8055             // we might need to allow it for textareas.. and some other items.
8056             // check e.getTarget().
8057
8058             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8059                 return true;
8060             }
8061
8062             Roo.log("keypress blocked");
8063
8064             e.preventDefault();
8065             return false;
8066         });
8067         
8068     },
8069     // private
8070     onSubmit : function(e){
8071         e.stopEvent();
8072     },
8073
8074      /**
8075      * Returns true if client-side validation on the form is successful.
8076      * @return Boolean
8077      */
8078     isValid : function(){
8079         var items = this.getItems();
8080         var valid = true;
8081         var target = false;
8082         
8083         items.each(function(f){
8084             
8085             if(f.validate()){
8086                 return;
8087             }
8088             
8089             Roo.log('invalid field: ' + f.name);
8090             
8091             valid = false;
8092
8093             if(!target && f.el.isVisible(true)){
8094                 target = f;
8095             }
8096            
8097         });
8098         
8099         if(this.errorMask && !valid){
8100             Roo.bootstrap.Form.popover.mask(this, target);
8101         }
8102         
8103         return valid;
8104     },
8105     
8106     /**
8107      * Returns true if any fields in this form have changed since their original load.
8108      * @return Boolean
8109      */
8110     isDirty : function(){
8111         var dirty = false;
8112         var items = this.getItems();
8113         items.each(function(f){
8114            if(f.isDirty()){
8115                dirty = true;
8116                return false;
8117            }
8118            return true;
8119         });
8120         return dirty;
8121     },
8122      /**
8123      * Performs a predefined action (submit or load) or custom actions you define on this form.
8124      * @param {String} actionName The name of the action type
8125      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8126      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8127      * accept other config options):
8128      * <pre>
8129 Property          Type             Description
8130 ----------------  ---------------  ----------------------------------------------------------------------------------
8131 url               String           The url for the action (defaults to the form's url)
8132 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8133 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8134 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8135                                    validate the form on the client (defaults to false)
8136      * </pre>
8137      * @return {BasicForm} this
8138      */
8139     doAction : function(action, options){
8140         if(typeof action == 'string'){
8141             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8142         }
8143         if(this.fireEvent('beforeaction', this, action) !== false){
8144             this.beforeAction(action);
8145             action.run.defer(100, action);
8146         }
8147         return this;
8148     },
8149
8150     // private
8151     beforeAction : function(action){
8152         var o = action.options;
8153         
8154         if(this.loadMask){
8155             
8156             if(this.maskBody){
8157                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8158             } else {
8159                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8160             }
8161         }
8162         // not really supported yet.. ??
8163
8164         //if(this.waitMsgTarget === true){
8165         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8166         //}else if(this.waitMsgTarget){
8167         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8168         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8169         //}else {
8170         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8171        // }
8172
8173     },
8174
8175     // private
8176     afterAction : function(action, success){
8177         this.activeAction = null;
8178         var o = action.options;
8179
8180         if(this.loadMask){
8181             
8182             if(this.maskBody){
8183                 Roo.get(document.body).unmask();
8184             } else {
8185                 this.el.unmask();
8186             }
8187         }
8188         
8189         //if(this.waitMsgTarget === true){
8190 //            this.el.unmask();
8191         //}else if(this.waitMsgTarget){
8192         //    this.waitMsgTarget.unmask();
8193         //}else{
8194         //    Roo.MessageBox.updateProgress(1);
8195         //    Roo.MessageBox.hide();
8196        // }
8197         //
8198         if(success){
8199             if(o.reset){
8200                 this.reset();
8201             }
8202             Roo.callback(o.success, o.scope, [this, action]);
8203             this.fireEvent('actioncomplete', this, action);
8204
8205         }else{
8206
8207             // failure condition..
8208             // we have a scenario where updates need confirming.
8209             // eg. if a locking scenario exists..
8210             // we look for { errors : { needs_confirm : true }} in the response.
8211             if (
8212                 (typeof(action.result) != 'undefined')  &&
8213                 (typeof(action.result.errors) != 'undefined')  &&
8214                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8215            ){
8216                 var _t = this;
8217                 Roo.log("not supported yet");
8218                  /*
8219
8220                 Roo.MessageBox.confirm(
8221                     "Change requires confirmation",
8222                     action.result.errorMsg,
8223                     function(r) {
8224                         if (r != 'yes') {
8225                             return;
8226                         }
8227                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8228                     }
8229
8230                 );
8231                 */
8232
8233
8234                 return;
8235             }
8236
8237             Roo.callback(o.failure, o.scope, [this, action]);
8238             // show an error message if no failed handler is set..
8239             if (!this.hasListener('actionfailed')) {
8240                 Roo.log("need to add dialog support");
8241                 /*
8242                 Roo.MessageBox.alert("Error",
8243                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8244                         action.result.errorMsg :
8245                         "Saving Failed, please check your entries or try again"
8246                 );
8247                 */
8248             }
8249
8250             this.fireEvent('actionfailed', this, action);
8251         }
8252
8253     },
8254     /**
8255      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8256      * @param {String} id The value to search for
8257      * @return Field
8258      */
8259     findField : function(id){
8260         var items = this.getItems();
8261         var field = items.get(id);
8262         if(!field){
8263              items.each(function(f){
8264                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8265                     field = f;
8266                     return false;
8267                 }
8268                 return true;
8269             });
8270         }
8271         return field || null;
8272     },
8273      /**
8274      * Mark fields in this form invalid in bulk.
8275      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8276      * @return {BasicForm} this
8277      */
8278     markInvalid : function(errors){
8279         if(errors instanceof Array){
8280             for(var i = 0, len = errors.length; i < len; i++){
8281                 var fieldError = errors[i];
8282                 var f = this.findField(fieldError.id);
8283                 if(f){
8284                     f.markInvalid(fieldError.msg);
8285                 }
8286             }
8287         }else{
8288             var field, id;
8289             for(id in errors){
8290                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8291                     field.markInvalid(errors[id]);
8292                 }
8293             }
8294         }
8295         //Roo.each(this.childForms || [], function (f) {
8296         //    f.markInvalid(errors);
8297         //});
8298
8299         return this;
8300     },
8301
8302     /**
8303      * Set values for fields in this form in bulk.
8304      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8305      * @return {BasicForm} this
8306      */
8307     setValues : function(values){
8308         if(values instanceof Array){ // array of objects
8309             for(var i = 0, len = values.length; i < len; i++){
8310                 var v = values[i];
8311                 var f = this.findField(v.id);
8312                 if(f){
8313                     f.setValue(v.value);
8314                     if(this.trackResetOnLoad){
8315                         f.originalValue = f.getValue();
8316                     }
8317                 }
8318             }
8319         }else{ // object hash
8320             var field, id;
8321             for(id in values){
8322                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8323
8324                     if (field.setFromData &&
8325                         field.valueField &&
8326                         field.displayField &&
8327                         // combos' with local stores can
8328                         // be queried via setValue()
8329                         // to set their value..
8330                         (field.store && !field.store.isLocal)
8331                         ) {
8332                         // it's a combo
8333                         var sd = { };
8334                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8335                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8336                         field.setFromData(sd);
8337
8338                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8339                         
8340                         field.setFromData(values);
8341                         
8342                     } else {
8343                         field.setValue(values[id]);
8344                     }
8345
8346
8347                     if(this.trackResetOnLoad){
8348                         field.originalValue = field.getValue();
8349                     }
8350                 }
8351             }
8352         }
8353
8354         //Roo.each(this.childForms || [], function (f) {
8355         //    f.setValues(values);
8356         //});
8357
8358         return this;
8359     },
8360
8361     /**
8362      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8363      * they are returned as an array.
8364      * @param {Boolean} asString
8365      * @return {Object}
8366      */
8367     getValues : function(asString){
8368         //if (this.childForms) {
8369             // copy values from the child forms
8370         //    Roo.each(this.childForms, function (f) {
8371         //        this.setValues(f.getValues());
8372         //    }, this);
8373         //}
8374
8375
8376
8377         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8378         if(asString === true){
8379             return fs;
8380         }
8381         return Roo.urlDecode(fs);
8382     },
8383
8384     /**
8385      * Returns the fields in this form as an object with key/value pairs.
8386      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8387      * @return {Object}
8388      */
8389     getFieldValues : function(with_hidden)
8390     {
8391         var items = this.getItems();
8392         var ret = {};
8393         items.each(function(f){
8394             
8395             if (!f.getName()) {
8396                 return;
8397             }
8398             
8399             var v = f.getValue();
8400             
8401             if (f.inputType =='radio') {
8402                 if (typeof(ret[f.getName()]) == 'undefined') {
8403                     ret[f.getName()] = ''; // empty..
8404                 }
8405
8406                 if (!f.el.dom.checked) {
8407                     return;
8408
8409                 }
8410                 v = f.el.dom.value;
8411
8412             }
8413             
8414             if(f.xtype == 'MoneyField'){
8415                 ret[f.currencyName] = f.getCurrency();
8416             }
8417
8418             // not sure if this supported any more..
8419             if ((typeof(v) == 'object') && f.getRawValue) {
8420                 v = f.getRawValue() ; // dates..
8421             }
8422             // combo boxes where name != hiddenName...
8423             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8424                 ret[f.name] = f.getRawValue();
8425             }
8426             ret[f.getName()] = v;
8427         });
8428
8429         return ret;
8430     },
8431
8432     /**
8433      * Clears all invalid messages in this form.
8434      * @return {BasicForm} this
8435      */
8436     clearInvalid : function(){
8437         var items = this.getItems();
8438
8439         items.each(function(f){
8440            f.clearInvalid();
8441         });
8442
8443         return this;
8444     },
8445
8446     /**
8447      * Resets this form.
8448      * @return {BasicForm} this
8449      */
8450     reset : function(){
8451         var items = this.getItems();
8452         items.each(function(f){
8453             f.reset();
8454         });
8455
8456         Roo.each(this.childForms || [], function (f) {
8457             f.reset();
8458         });
8459
8460
8461         return this;
8462     },
8463     
8464     getItems : function()
8465     {
8466         var r=new Roo.util.MixedCollection(false, function(o){
8467             return o.id || (o.id = Roo.id());
8468         });
8469         var iter = function(el) {
8470             if (el.inputEl) {
8471                 r.add(el);
8472             }
8473             if (!el.items) {
8474                 return;
8475             }
8476             Roo.each(el.items,function(e) {
8477                 iter(e);
8478             });
8479         };
8480
8481         iter(this);
8482         return r;
8483     },
8484     
8485     hideFields : function(items)
8486     {
8487         Roo.each(items, function(i){
8488             
8489             var f = this.findField(i);
8490             
8491             if(!f){
8492                 return;
8493             }
8494             
8495             f.hide();
8496             
8497         }, this);
8498     },
8499     
8500     showFields : function(items)
8501     {
8502         Roo.each(items, function(i){
8503             
8504             var f = this.findField(i);
8505             
8506             if(!f){
8507                 return;
8508             }
8509             
8510             f.show();
8511             
8512         }, this);
8513     }
8514
8515 });
8516
8517 Roo.apply(Roo.bootstrap.Form, {
8518     
8519     popover : {
8520         
8521         padding : 5,
8522         
8523         isApplied : false,
8524         
8525         isMasked : false,
8526         
8527         form : false,
8528         
8529         target : false,
8530         
8531         toolTip : false,
8532         
8533         intervalID : false,
8534         
8535         maskEl : false,
8536         
8537         apply : function()
8538         {
8539             if(this.isApplied){
8540                 return;
8541             }
8542             
8543             this.maskEl = {
8544                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8545                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8546                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8547                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8548             };
8549             
8550             this.maskEl.top.enableDisplayMode("block");
8551             this.maskEl.left.enableDisplayMode("block");
8552             this.maskEl.bottom.enableDisplayMode("block");
8553             this.maskEl.right.enableDisplayMode("block");
8554             
8555             this.toolTip = new Roo.bootstrap.Tooltip({
8556                 cls : 'roo-form-error-popover',
8557                 alignment : {
8558                     'left' : ['r-l', [-2,0], 'right'],
8559                     'right' : ['l-r', [2,0], 'left'],
8560                     'bottom' : ['tl-bl', [0,2], 'top'],
8561                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8562                 }
8563             });
8564             
8565             this.toolTip.render(Roo.get(document.body));
8566
8567             this.toolTip.el.enableDisplayMode("block");
8568             
8569             Roo.get(document.body).on('click', function(){
8570                 this.unmask();
8571             }, this);
8572             
8573             Roo.get(document.body).on('touchstart', function(){
8574                 this.unmask();
8575             }, this);
8576             
8577             this.isApplied = true
8578         },
8579         
8580         mask : function(form, target)
8581         {
8582             this.form = form;
8583             
8584             this.target = target;
8585             
8586             if(!this.form.errorMask || !target.el){
8587                 return;
8588             }
8589             
8590             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8591             
8592             Roo.log(scrollable);
8593             
8594             var ot = this.target.el.calcOffsetsTo(scrollable);
8595             
8596             var scrollTo = ot[1] - this.form.maskOffset;
8597             
8598             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8599             
8600             scrollable.scrollTo('top', scrollTo);
8601             
8602             var box = this.target.el.getBox();
8603             Roo.log(box);
8604             var zIndex = Roo.bootstrap.Modal.zIndex++;
8605
8606             
8607             this.maskEl.top.setStyle('position', 'absolute');
8608             this.maskEl.top.setStyle('z-index', zIndex);
8609             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8610             this.maskEl.top.setLeft(0);
8611             this.maskEl.top.setTop(0);
8612             this.maskEl.top.show();
8613             
8614             this.maskEl.left.setStyle('position', 'absolute');
8615             this.maskEl.left.setStyle('z-index', zIndex);
8616             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8617             this.maskEl.left.setLeft(0);
8618             this.maskEl.left.setTop(box.y - this.padding);
8619             this.maskEl.left.show();
8620
8621             this.maskEl.bottom.setStyle('position', 'absolute');
8622             this.maskEl.bottom.setStyle('z-index', zIndex);
8623             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8624             this.maskEl.bottom.setLeft(0);
8625             this.maskEl.bottom.setTop(box.bottom + this.padding);
8626             this.maskEl.bottom.show();
8627
8628             this.maskEl.right.setStyle('position', 'absolute');
8629             this.maskEl.right.setStyle('z-index', zIndex);
8630             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8631             this.maskEl.right.setLeft(box.right + this.padding);
8632             this.maskEl.right.setTop(box.y - this.padding);
8633             this.maskEl.right.show();
8634
8635             this.toolTip.bindEl = this.target.el;
8636
8637             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8638
8639             var tip = this.target.blankText;
8640
8641             if(this.target.getValue() !== '' ) {
8642                 
8643                 if (this.target.invalidText.length) {
8644                     tip = this.target.invalidText;
8645                 } else if (this.target.regexText.length){
8646                     tip = this.target.regexText;
8647                 }
8648             }
8649
8650             this.toolTip.show(tip);
8651
8652             this.intervalID = window.setInterval(function() {
8653                 Roo.bootstrap.Form.popover.unmask();
8654             }, 10000);
8655
8656             window.onwheel = function(){ return false;};
8657             
8658             (function(){ this.isMasked = true; }).defer(500, this);
8659             
8660         },
8661         
8662         unmask : function()
8663         {
8664             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8665                 return;
8666             }
8667             
8668             this.maskEl.top.setStyle('position', 'absolute');
8669             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8670             this.maskEl.top.hide();
8671
8672             this.maskEl.left.setStyle('position', 'absolute');
8673             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8674             this.maskEl.left.hide();
8675
8676             this.maskEl.bottom.setStyle('position', 'absolute');
8677             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8678             this.maskEl.bottom.hide();
8679
8680             this.maskEl.right.setStyle('position', 'absolute');
8681             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8682             this.maskEl.right.hide();
8683             
8684             this.toolTip.hide();
8685             
8686             this.toolTip.el.hide();
8687             
8688             window.onwheel = function(){ return true;};
8689             
8690             if(this.intervalID){
8691                 window.clearInterval(this.intervalID);
8692                 this.intervalID = false;
8693             }
8694             
8695             this.isMasked = false;
8696             
8697         }
8698         
8699     }
8700     
8701 });
8702
8703 /*
8704  * Based on:
8705  * Ext JS Library 1.1.1
8706  * Copyright(c) 2006-2007, Ext JS, LLC.
8707  *
8708  * Originally Released Under LGPL - original licence link has changed is not relivant.
8709  *
8710  * Fork - LGPL
8711  * <script type="text/javascript">
8712  */
8713 /**
8714  * @class Roo.form.VTypes
8715  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8716  * @singleton
8717  */
8718 Roo.form.VTypes = function(){
8719     // closure these in so they are only created once.
8720     var alpha = /^[a-zA-Z_]+$/;
8721     var alphanum = /^[a-zA-Z0-9_]+$/;
8722     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8723     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8724
8725     // All these messages and functions are configurable
8726     return {
8727         /**
8728          * The function used to validate email addresses
8729          * @param {String} value The email address
8730          */
8731         'email' : function(v){
8732             return email.test(v);
8733         },
8734         /**
8735          * The error text to display when the email validation function returns false
8736          * @type String
8737          */
8738         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8739         /**
8740          * The keystroke filter mask to be applied on email input
8741          * @type RegExp
8742          */
8743         'emailMask' : /[a-z0-9_\.\-@]/i,
8744
8745         /**
8746          * The function used to validate URLs
8747          * @param {String} value The URL
8748          */
8749         'url' : function(v){
8750             return url.test(v);
8751         },
8752         /**
8753          * The error text to display when the url validation function returns false
8754          * @type String
8755          */
8756         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8757         
8758         /**
8759          * The function used to validate alpha values
8760          * @param {String} value The value
8761          */
8762         'alpha' : function(v){
8763             return alpha.test(v);
8764         },
8765         /**
8766          * The error text to display when the alpha validation function returns false
8767          * @type String
8768          */
8769         'alphaText' : 'This field should only contain letters and _',
8770         /**
8771          * The keystroke filter mask to be applied on alpha input
8772          * @type RegExp
8773          */
8774         'alphaMask' : /[a-z_]/i,
8775
8776         /**
8777          * The function used to validate alphanumeric values
8778          * @param {String} value The value
8779          */
8780         'alphanum' : function(v){
8781             return alphanum.test(v);
8782         },
8783         /**
8784          * The error text to display when the alphanumeric validation function returns false
8785          * @type String
8786          */
8787         'alphanumText' : 'This field should only contain letters, numbers and _',
8788         /**
8789          * The keystroke filter mask to be applied on alphanumeric input
8790          * @type RegExp
8791          */
8792         'alphanumMask' : /[a-z0-9_]/i
8793     };
8794 }();/*
8795  * - LGPL
8796  *
8797  * Input
8798  * 
8799  */
8800
8801 /**
8802  * @class Roo.bootstrap.Input
8803  * @extends Roo.bootstrap.Component
8804  * Bootstrap Input class
8805  * @cfg {Boolean} disabled is it disabled
8806  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8807  * @cfg {String} name name of the input
8808  * @cfg {string} fieldLabel - the label associated
8809  * @cfg {string} placeholder - placeholder to put in text.
8810  * @cfg {string}  before - input group add on before
8811  * @cfg {string} after - input group add on after
8812  * @cfg {string} size - (lg|sm) or leave empty..
8813  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8814  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8815  * @cfg {Number} md colspan out of 12 for computer-sized screens
8816  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8817  * @cfg {string} value default value of the input
8818  * @cfg {Number} labelWidth set the width of label 
8819  * @cfg {Number} labellg set the width of label (1-12)
8820  * @cfg {Number} labelmd set the width of label (1-12)
8821  * @cfg {Number} labelsm set the width of label (1-12)
8822  * @cfg {Number} labelxs set the width of label (1-12)
8823  * @cfg {String} labelAlign (top|left)
8824  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8825  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8826  * @cfg {String} indicatorpos (left|right) default left
8827  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8828  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8829
8830  * @cfg {String} align (left|center|right) Default left
8831  * @cfg {Boolean} forceFeedback (true|false) Default false
8832  * 
8833  * @constructor
8834  * Create a new Input
8835  * @param {Object} config The config object
8836  */
8837
8838 Roo.bootstrap.Input = function(config){
8839     
8840     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8841     
8842     this.addEvents({
8843         /**
8844          * @event focus
8845          * Fires when this field receives input focus.
8846          * @param {Roo.form.Field} this
8847          */
8848         focus : true,
8849         /**
8850          * @event blur
8851          * Fires when this field loses input focus.
8852          * @param {Roo.form.Field} this
8853          */
8854         blur : true,
8855         /**
8856          * @event specialkey
8857          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8858          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8859          * @param {Roo.form.Field} this
8860          * @param {Roo.EventObject} e The event object
8861          */
8862         specialkey : true,
8863         /**
8864          * @event change
8865          * Fires just before the field blurs if the field value has changed.
8866          * @param {Roo.form.Field} this
8867          * @param {Mixed} newValue The new value
8868          * @param {Mixed} oldValue The original value
8869          */
8870         change : true,
8871         /**
8872          * @event invalid
8873          * Fires after the field has been marked as invalid.
8874          * @param {Roo.form.Field} this
8875          * @param {String} msg The validation message
8876          */
8877         invalid : true,
8878         /**
8879          * @event valid
8880          * Fires after the field has been validated with no errors.
8881          * @param {Roo.form.Field} this
8882          */
8883         valid : true,
8884          /**
8885          * @event keyup
8886          * Fires after the key up
8887          * @param {Roo.form.Field} this
8888          * @param {Roo.EventObject}  e The event Object
8889          */
8890         keyup : true
8891     });
8892 };
8893
8894 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8895      /**
8896      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8897       automatic validation (defaults to "keyup").
8898      */
8899     validationEvent : "keyup",
8900      /**
8901      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8902      */
8903     validateOnBlur : true,
8904     /**
8905      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8906      */
8907     validationDelay : 250,
8908      /**
8909      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8910      */
8911     focusClass : "x-form-focus",  // not needed???
8912     
8913        
8914     /**
8915      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8916      */
8917     invalidClass : "has-warning",
8918     
8919     /**
8920      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8921      */
8922     validClass : "has-success",
8923     
8924     /**
8925      * @cfg {Boolean} hasFeedback (true|false) default true
8926      */
8927     hasFeedback : true,
8928     
8929     /**
8930      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8931      */
8932     invalidFeedbackClass : "glyphicon-warning-sign",
8933     
8934     /**
8935      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8936      */
8937     validFeedbackClass : "glyphicon-ok",
8938     
8939     /**
8940      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8941      */
8942     selectOnFocus : false,
8943     
8944      /**
8945      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8946      */
8947     maskRe : null,
8948        /**
8949      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8950      */
8951     vtype : null,
8952     
8953       /**
8954      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8955      */
8956     disableKeyFilter : false,
8957     
8958        /**
8959      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8960      */
8961     disabled : false,
8962      /**
8963      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8964      */
8965     allowBlank : true,
8966     /**
8967      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8968      */
8969     blankText : "Please complete this mandatory field",
8970     
8971      /**
8972      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8973      */
8974     minLength : 0,
8975     /**
8976      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8977      */
8978     maxLength : Number.MAX_VALUE,
8979     /**
8980      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8981      */
8982     minLengthText : "The minimum length for this field is {0}",
8983     /**
8984      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8985      */
8986     maxLengthText : "The maximum length for this field is {0}",
8987   
8988     
8989     /**
8990      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8991      * If available, this function will be called only after the basic validators all return true, and will be passed the
8992      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8993      */
8994     validator : null,
8995     /**
8996      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8997      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8998      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8999      */
9000     regex : null,
9001     /**
9002      * @cfg {String} regexText -- Depricated - use Invalid Text
9003      */
9004     regexText : "",
9005     
9006     /**
9007      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9008      */
9009     invalidText : "",
9010     
9011     
9012     
9013     autocomplete: false,
9014     
9015     
9016     fieldLabel : '',
9017     inputType : 'text',
9018     
9019     name : false,
9020     placeholder: false,
9021     before : false,
9022     after : false,
9023     size : false,
9024     hasFocus : false,
9025     preventMark: false,
9026     isFormField : true,
9027     value : '',
9028     labelWidth : 2,
9029     labelAlign : false,
9030     readOnly : false,
9031     align : false,
9032     formatedValue : false,
9033     forceFeedback : false,
9034     
9035     indicatorpos : 'left',
9036     
9037     labellg : 0,
9038     labelmd : 0,
9039     labelsm : 0,
9040     labelxs : 0,
9041     
9042     capture : '',
9043     accept : '',
9044     
9045     parentLabelAlign : function()
9046     {
9047         var parent = this;
9048         while (parent.parent()) {
9049             parent = parent.parent();
9050             if (typeof(parent.labelAlign) !='undefined') {
9051                 return parent.labelAlign;
9052             }
9053         }
9054         return 'left';
9055         
9056     },
9057     
9058     getAutoCreate : function()
9059     {
9060         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9061         
9062         var id = Roo.id();
9063         
9064         var cfg = {};
9065         
9066         if(this.inputType != 'hidden'){
9067             cfg.cls = 'form-group' //input-group
9068         }
9069         
9070         var input =  {
9071             tag: 'input',
9072             id : id,
9073             type : this.inputType,
9074             value : this.value,
9075             cls : 'form-control',
9076             placeholder : this.placeholder || '',
9077             autocomplete : this.autocomplete || 'new-password'
9078         };
9079         
9080         if(this.capture.length){
9081             input.capture = this.capture;
9082         }
9083         
9084         if(this.accept.length){
9085             input.accept = this.accept + "/*";
9086         }
9087         
9088         if(this.align){
9089             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9090         }
9091         
9092         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9093             input.maxLength = this.maxLength;
9094         }
9095         
9096         if (this.disabled) {
9097             input.disabled=true;
9098         }
9099         
9100         if (this.readOnly) {
9101             input.readonly=true;
9102         }
9103         
9104         if (this.name) {
9105             input.name = this.name;
9106         }
9107         
9108         if (this.size) {
9109             input.cls += ' input-' + this.size;
9110         }
9111         
9112         var settings=this;
9113         ['xs','sm','md','lg'].map(function(size){
9114             if (settings[size]) {
9115                 cfg.cls += ' col-' + size + '-' + settings[size];
9116             }
9117         });
9118         
9119         var inputblock = input;
9120         
9121         var feedback = {
9122             tag: 'span',
9123             cls: 'glyphicon form-control-feedback'
9124         };
9125             
9126         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9127             
9128             inputblock = {
9129                 cls : 'has-feedback',
9130                 cn :  [
9131                     input,
9132                     feedback
9133                 ] 
9134             };  
9135         }
9136         
9137         if (this.before || this.after) {
9138             
9139             inputblock = {
9140                 cls : 'input-group',
9141                 cn :  [] 
9142             };
9143             
9144             if (this.before && typeof(this.before) == 'string') {
9145                 
9146                 inputblock.cn.push({
9147                     tag :'span',
9148                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9149                     html : this.before
9150                 });
9151             }
9152             if (this.before && typeof(this.before) == 'object') {
9153                 this.before = Roo.factory(this.before);
9154                 
9155                 inputblock.cn.push({
9156                     tag :'span',
9157                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9158                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9159                 });
9160             }
9161             
9162             inputblock.cn.push(input);
9163             
9164             if (this.after && typeof(this.after) == 'string') {
9165                 inputblock.cn.push({
9166                     tag :'span',
9167                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9168                     html : this.after
9169                 });
9170             }
9171             if (this.after && typeof(this.after) == 'object') {
9172                 this.after = Roo.factory(this.after);
9173                 
9174                 inputblock.cn.push({
9175                     tag :'span',
9176                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9177                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9178                 });
9179             }
9180             
9181             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9182                 inputblock.cls += ' has-feedback';
9183                 inputblock.cn.push(feedback);
9184             }
9185         };
9186         var indicator = {
9187             tag : 'i',
9188             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9189             tooltip : 'This field is required'
9190         };
9191         if (Roo.bootstrap.version == 4) {
9192             indicator = {
9193                 tag : 'i',
9194                 style : 'display-none'
9195             };
9196         }
9197         if (align ==='left' && this.fieldLabel.length) {
9198             
9199             cfg.cls += ' roo-form-group-label-left row';
9200             
9201             cfg.cn = [
9202                 indicator,
9203                 {
9204                     tag: 'label',
9205                     'for' :  id,
9206                     cls : 'control-label col-form-label',
9207                     html : this.fieldLabel
9208
9209                 },
9210                 {
9211                     cls : "", 
9212                     cn: [
9213                         inputblock
9214                     ]
9215                 }
9216             ];
9217             
9218             var labelCfg = cfg.cn[1];
9219             var contentCfg = cfg.cn[2];
9220             
9221             if(this.indicatorpos == 'right'){
9222                 cfg.cn = [
9223                     {
9224                         tag: 'label',
9225                         'for' :  id,
9226                         cls : 'control-label col-form-label',
9227                         cn : [
9228                             {
9229                                 tag : 'span',
9230                                 html : this.fieldLabel
9231                             },
9232                             indicator
9233                         ]
9234                     },
9235                     {
9236                         cls : "",
9237                         cn: [
9238                             inputblock
9239                         ]
9240                     }
9241
9242                 ];
9243                 
9244                 labelCfg = cfg.cn[0];
9245                 contentCfg = cfg.cn[1];
9246             
9247             }
9248             
9249             if(this.labelWidth > 12){
9250                 labelCfg.style = "width: " + this.labelWidth + 'px';
9251             }
9252             
9253             if(this.labelWidth < 13 && this.labelmd == 0){
9254                 this.labelmd = this.labelWidth;
9255             }
9256             
9257             if(this.labellg > 0){
9258                 labelCfg.cls += ' col-lg-' + this.labellg;
9259                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9260             }
9261             
9262             if(this.labelmd > 0){
9263                 labelCfg.cls += ' col-md-' + this.labelmd;
9264                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9265             }
9266             
9267             if(this.labelsm > 0){
9268                 labelCfg.cls += ' col-sm-' + this.labelsm;
9269                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9270             }
9271             
9272             if(this.labelxs > 0){
9273                 labelCfg.cls += ' col-xs-' + this.labelxs;
9274                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9275             }
9276             
9277             
9278         } else if ( this.fieldLabel.length) {
9279                 
9280             cfg.cn = [
9281                 {
9282                     tag : 'i',
9283                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9284                     tooltip : 'This field is required'
9285                 },
9286                 {
9287                     tag: 'label',
9288                    //cls : 'input-group-addon',
9289                     html : this.fieldLabel
9290
9291                 },
9292
9293                inputblock
9294
9295            ];
9296            
9297            if(this.indicatorpos == 'right'){
9298                 
9299                 cfg.cn = [
9300                     {
9301                         tag: 'label',
9302                        //cls : 'input-group-addon',
9303                         html : this.fieldLabel
9304
9305                     },
9306                     {
9307                         tag : 'i',
9308                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9309                         tooltip : 'This field is required'
9310                     },
9311
9312                    inputblock
9313
9314                ];
9315
9316             }
9317
9318         } else {
9319             
9320             cfg.cn = [
9321
9322                     inputblock
9323
9324             ];
9325                 
9326                 
9327         };
9328         
9329         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9330            cfg.cls += ' navbar-form';
9331         }
9332         
9333         if (this.parentType === 'NavGroup') {
9334            cfg.cls += ' navbar-form';
9335            cfg.tag = 'li';
9336         }
9337         
9338         return cfg;
9339         
9340     },
9341     /**
9342      * return the real input element.
9343      */
9344     inputEl: function ()
9345     {
9346         return this.el.select('input.form-control',true).first();
9347     },
9348     
9349     tooltipEl : function()
9350     {
9351         return this.inputEl();
9352     },
9353     
9354     indicatorEl : function()
9355     {
9356         if (Roo.bootstrap.version == 4) {
9357             return false; // not enabled in v4 yet.
9358         }
9359         
9360         var indicator = this.el.select('i.roo-required-indicator',true).first();
9361         
9362         if(!indicator){
9363             return false;
9364         }
9365         
9366         return indicator;
9367         
9368     },
9369     
9370     setDisabled : function(v)
9371     {
9372         var i  = this.inputEl().dom;
9373         if (!v) {
9374             i.removeAttribute('disabled');
9375             return;
9376             
9377         }
9378         i.setAttribute('disabled','true');
9379     },
9380     initEvents : function()
9381     {
9382           
9383         this.inputEl().on("keydown" , this.fireKey,  this);
9384         this.inputEl().on("focus", this.onFocus,  this);
9385         this.inputEl().on("blur", this.onBlur,  this);
9386         
9387         this.inputEl().relayEvent('keyup', this);
9388         
9389         this.indicator = this.indicatorEl();
9390         
9391         if(this.indicator){
9392             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9393         }
9394  
9395         // reference to original value for reset
9396         this.originalValue = this.getValue();
9397         //Roo.form.TextField.superclass.initEvents.call(this);
9398         if(this.validationEvent == 'keyup'){
9399             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9400             this.inputEl().on('keyup', this.filterValidation, this);
9401         }
9402         else if(this.validationEvent !== false){
9403             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9404         }
9405         
9406         if(this.selectOnFocus){
9407             this.on("focus", this.preFocus, this);
9408             
9409         }
9410         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9411             this.inputEl().on("keypress", this.filterKeys, this);
9412         } else {
9413             this.inputEl().relayEvent('keypress', this);
9414         }
9415        /* if(this.grow){
9416             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9417             this.el.on("click", this.autoSize,  this);
9418         }
9419         */
9420         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9421             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9422         }
9423         
9424         if (typeof(this.before) == 'object') {
9425             this.before.render(this.el.select('.roo-input-before',true).first());
9426         }
9427         if (typeof(this.after) == 'object') {
9428             this.after.render(this.el.select('.roo-input-after',true).first());
9429         }
9430         
9431         this.inputEl().on('change', this.onChange, this);
9432         
9433     },
9434     filterValidation : function(e){
9435         if(!e.isNavKeyPress()){
9436             this.validationTask.delay(this.validationDelay);
9437         }
9438     },
9439      /**
9440      * Validates the field value
9441      * @return {Boolean} True if the value is valid, else false
9442      */
9443     validate : function(){
9444         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9445         if(this.disabled || this.validateValue(this.getRawValue())){
9446             this.markValid();
9447             return true;
9448         }
9449         
9450         this.markInvalid();
9451         return false;
9452     },
9453     
9454     
9455     /**
9456      * Validates a value according to the field's validation rules and marks the field as invalid
9457      * if the validation fails
9458      * @param {Mixed} value The value to validate
9459      * @return {Boolean} True if the value is valid, else false
9460      */
9461     validateValue : function(value)
9462     {
9463         if(this.getVisibilityEl().hasClass('hidden')){
9464             return true;
9465         }
9466         
9467         if(value.length < 1)  { // if it's blank
9468             if(this.allowBlank){
9469                 return true;
9470             }
9471             return false;
9472         }
9473         
9474         if(value.length < this.minLength){
9475             return false;
9476         }
9477         if(value.length > this.maxLength){
9478             return false;
9479         }
9480         if(this.vtype){
9481             var vt = Roo.form.VTypes;
9482             if(!vt[this.vtype](value, this)){
9483                 return false;
9484             }
9485         }
9486         if(typeof this.validator == "function"){
9487             var msg = this.validator(value);
9488             if(msg !== true){
9489                 return false;
9490             }
9491             if (typeof(msg) == 'string') {
9492                 this.invalidText = msg;
9493             }
9494         }
9495         
9496         if(this.regex && !this.regex.test(value)){
9497             return false;
9498         }
9499         
9500         return true;
9501     },
9502     
9503      // private
9504     fireKey : function(e){
9505         //Roo.log('field ' + e.getKey());
9506         if(e.isNavKeyPress()){
9507             this.fireEvent("specialkey", this, e);
9508         }
9509     },
9510     focus : function (selectText){
9511         if(this.rendered){
9512             this.inputEl().focus();
9513             if(selectText === true){
9514                 this.inputEl().dom.select();
9515             }
9516         }
9517         return this;
9518     } ,
9519     
9520     onFocus : function(){
9521         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9522            // this.el.addClass(this.focusClass);
9523         }
9524         if(!this.hasFocus){
9525             this.hasFocus = true;
9526             this.startValue = this.getValue();
9527             this.fireEvent("focus", this);
9528         }
9529     },
9530     
9531     beforeBlur : Roo.emptyFn,
9532
9533     
9534     // private
9535     onBlur : function(){
9536         this.beforeBlur();
9537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9538             //this.el.removeClass(this.focusClass);
9539         }
9540         this.hasFocus = false;
9541         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9542             this.validate();
9543         }
9544         var v = this.getValue();
9545         if(String(v) !== String(this.startValue)){
9546             this.fireEvent('change', this, v, this.startValue);
9547         }
9548         this.fireEvent("blur", this);
9549     },
9550     
9551     onChange : function(e)
9552     {
9553         var v = this.getValue();
9554         if(String(v) !== String(this.startValue)){
9555             this.fireEvent('change', this, v, this.startValue);
9556         }
9557         
9558     },
9559     
9560     /**
9561      * Resets the current field value to the originally loaded value and clears any validation messages
9562      */
9563     reset : function(){
9564         this.setValue(this.originalValue);
9565         this.validate();
9566     },
9567      /**
9568      * Returns the name of the field
9569      * @return {Mixed} name The name field
9570      */
9571     getName: function(){
9572         return this.name;
9573     },
9574      /**
9575      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9576      * @return {Mixed} value The field value
9577      */
9578     getValue : function(){
9579         
9580         var v = this.inputEl().getValue();
9581         
9582         return v;
9583     },
9584     /**
9585      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9586      * @return {Mixed} value The field value
9587      */
9588     getRawValue : function(){
9589         var v = this.inputEl().getValue();
9590         
9591         return v;
9592     },
9593     
9594     /**
9595      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9596      * @param {Mixed} value The value to set
9597      */
9598     setRawValue : function(v){
9599         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9600     },
9601     
9602     selectText : function(start, end){
9603         var v = this.getRawValue();
9604         if(v.length > 0){
9605             start = start === undefined ? 0 : start;
9606             end = end === undefined ? v.length : end;
9607             var d = this.inputEl().dom;
9608             if(d.setSelectionRange){
9609                 d.setSelectionRange(start, end);
9610             }else if(d.createTextRange){
9611                 var range = d.createTextRange();
9612                 range.moveStart("character", start);
9613                 range.moveEnd("character", v.length-end);
9614                 range.select();
9615             }
9616         }
9617     },
9618     
9619     /**
9620      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9621      * @param {Mixed} value The value to set
9622      */
9623     setValue : function(v){
9624         this.value = v;
9625         if(this.rendered){
9626             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9627             this.validate();
9628         }
9629     },
9630     
9631     /*
9632     processValue : function(value){
9633         if(this.stripCharsRe){
9634             var newValue = value.replace(this.stripCharsRe, '');
9635             if(newValue !== value){
9636                 this.setRawValue(newValue);
9637                 return newValue;
9638             }
9639         }
9640         return value;
9641     },
9642   */
9643     preFocus : function(){
9644         
9645         if(this.selectOnFocus){
9646             this.inputEl().dom.select();
9647         }
9648     },
9649     filterKeys : function(e){
9650         var k = e.getKey();
9651         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9652             return;
9653         }
9654         var c = e.getCharCode(), cc = String.fromCharCode(c);
9655         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9656             return;
9657         }
9658         if(!this.maskRe.test(cc)){
9659             e.stopEvent();
9660         }
9661     },
9662      /**
9663      * Clear any invalid styles/messages for this field
9664      */
9665     clearInvalid : function(){
9666         
9667         if(!this.el || this.preventMark){ // not rendered
9668             return;
9669         }
9670         
9671      
9672         this.el.removeClass(this.invalidClass);
9673         
9674         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9675             
9676             var feedback = this.el.select('.form-control-feedback', true).first();
9677             
9678             if(feedback){
9679                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9680             }
9681             
9682         }
9683         
9684         if(this.indicator){
9685             this.indicator.removeClass('visible');
9686             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9687         }
9688         
9689         this.fireEvent('valid', this);
9690     },
9691     
9692      /**
9693      * Mark this field as valid
9694      */
9695     markValid : function()
9696     {
9697         if(!this.el  || this.preventMark){ // not rendered...
9698             return;
9699         }
9700         
9701         this.el.removeClass([this.invalidClass, this.validClass]);
9702         
9703         var feedback = this.el.select('.form-control-feedback', true).first();
9704             
9705         if(feedback){
9706             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9707         }
9708         
9709         if(this.indicator){
9710             this.indicator.removeClass('visible');
9711             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9712         }
9713         
9714         if(this.disabled){
9715             return;
9716         }
9717         
9718         if(this.allowBlank && !this.getRawValue().length){
9719             return;
9720         }
9721         
9722         this.el.addClass(this.validClass);
9723         
9724         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9725             
9726             var feedback = this.el.select('.form-control-feedback', true).first();
9727             
9728             if(feedback){
9729                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9730                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9731             }
9732             
9733         }
9734         
9735         this.fireEvent('valid', this);
9736     },
9737     
9738      /**
9739      * Mark this field as invalid
9740      * @param {String} msg The validation message
9741      */
9742     markInvalid : function(msg)
9743     {
9744         if(!this.el  || this.preventMark){ // not rendered
9745             return;
9746         }
9747         
9748         this.el.removeClass([this.invalidClass, this.validClass]);
9749         
9750         var feedback = this.el.select('.form-control-feedback', true).first();
9751             
9752         if(feedback){
9753             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9754         }
9755
9756         if(this.disabled){
9757             return;
9758         }
9759         
9760         if(this.allowBlank && !this.getRawValue().length){
9761             return;
9762         }
9763         
9764         if(this.indicator){
9765             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9766             this.indicator.addClass('visible');
9767         }
9768         
9769         this.el.addClass(this.invalidClass);
9770         
9771         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9772             
9773             var feedback = this.el.select('.form-control-feedback', true).first();
9774             
9775             if(feedback){
9776                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9777                 
9778                 if(this.getValue().length || this.forceFeedback){
9779                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9780                 }
9781                 
9782             }
9783             
9784         }
9785         
9786         this.fireEvent('invalid', this, msg);
9787     },
9788     // private
9789     SafariOnKeyDown : function(event)
9790     {
9791         // this is a workaround for a password hang bug on chrome/ webkit.
9792         if (this.inputEl().dom.type != 'password') {
9793             return;
9794         }
9795         
9796         var isSelectAll = false;
9797         
9798         if(this.inputEl().dom.selectionEnd > 0){
9799             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9800         }
9801         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9802             event.preventDefault();
9803             this.setValue('');
9804             return;
9805         }
9806         
9807         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9808             
9809             event.preventDefault();
9810             // this is very hacky as keydown always get's upper case.
9811             //
9812             var cc = String.fromCharCode(event.getCharCode());
9813             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9814             
9815         }
9816     },
9817     adjustWidth : function(tag, w){
9818         tag = tag.toLowerCase();
9819         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9820             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9821                 if(tag == 'input'){
9822                     return w + 2;
9823                 }
9824                 if(tag == 'textarea'){
9825                     return w-2;
9826                 }
9827             }else if(Roo.isOpera){
9828                 if(tag == 'input'){
9829                     return w + 2;
9830                 }
9831                 if(tag == 'textarea'){
9832                     return w-2;
9833                 }
9834             }
9835         }
9836         return w;
9837     },
9838     
9839     setFieldLabel : function(v)
9840     {
9841         if(!this.rendered){
9842             return;
9843         }
9844         
9845         if(this.indicatorEl()){
9846             var ar = this.el.select('label > span',true);
9847             
9848             if (ar.elements.length) {
9849                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9850                 this.fieldLabel = v;
9851                 return;
9852             }
9853             
9854             var br = this.el.select('label',true);
9855             
9856             if(br.elements.length) {
9857                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9858                 this.fieldLabel = v;
9859                 return;
9860             }
9861             
9862             Roo.log('Cannot Found any of label > span || label in input');
9863             return;
9864         }
9865         
9866         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9867         this.fieldLabel = v;
9868         
9869         
9870     }
9871 });
9872
9873  
9874 /*
9875  * - LGPL
9876  *
9877  * Input
9878  * 
9879  */
9880
9881 /**
9882  * @class Roo.bootstrap.TextArea
9883  * @extends Roo.bootstrap.Input
9884  * Bootstrap TextArea class
9885  * @cfg {Number} cols Specifies the visible width of a text area
9886  * @cfg {Number} rows Specifies the visible number of lines in a text area
9887  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9888  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9889  * @cfg {string} html text
9890  * 
9891  * @constructor
9892  * Create a new TextArea
9893  * @param {Object} config The config object
9894  */
9895
9896 Roo.bootstrap.TextArea = function(config){
9897     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9898    
9899 };
9900
9901 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9902      
9903     cols : false,
9904     rows : 5,
9905     readOnly : false,
9906     warp : 'soft',
9907     resize : false,
9908     value: false,
9909     html: false,
9910     
9911     getAutoCreate : function(){
9912         
9913         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9914         
9915         var id = Roo.id();
9916         
9917         var cfg = {};
9918         
9919         if(this.inputType != 'hidden'){
9920             cfg.cls = 'form-group' //input-group
9921         }
9922         
9923         var input =  {
9924             tag: 'textarea',
9925             id : id,
9926             warp : this.warp,
9927             rows : this.rows,
9928             value : this.value || '',
9929             html: this.html || '',
9930             cls : 'form-control',
9931             placeholder : this.placeholder || '' 
9932             
9933         };
9934         
9935         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9936             input.maxLength = this.maxLength;
9937         }
9938         
9939         if(this.resize){
9940             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9941         }
9942         
9943         if(this.cols){
9944             input.cols = this.cols;
9945         }
9946         
9947         if (this.readOnly) {
9948             input.readonly = true;
9949         }
9950         
9951         if (this.name) {
9952             input.name = this.name;
9953         }
9954         
9955         if (this.size) {
9956             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9957         }
9958         
9959         var settings=this;
9960         ['xs','sm','md','lg'].map(function(size){
9961             if (settings[size]) {
9962                 cfg.cls += ' col-' + size + '-' + settings[size];
9963             }
9964         });
9965         
9966         var inputblock = input;
9967         
9968         if(this.hasFeedback && !this.allowBlank){
9969             
9970             var feedback = {
9971                 tag: 'span',
9972                 cls: 'glyphicon form-control-feedback'
9973             };
9974
9975             inputblock = {
9976                 cls : 'has-feedback',
9977                 cn :  [
9978                     input,
9979                     feedback
9980                 ] 
9981             };  
9982         }
9983         
9984         
9985         if (this.before || this.after) {
9986             
9987             inputblock = {
9988                 cls : 'input-group',
9989                 cn :  [] 
9990             };
9991             if (this.before) {
9992                 inputblock.cn.push({
9993                     tag :'span',
9994                     cls : 'input-group-addon',
9995                     html : this.before
9996                 });
9997             }
9998             
9999             inputblock.cn.push(input);
10000             
10001             if(this.hasFeedback && !this.allowBlank){
10002                 inputblock.cls += ' has-feedback';
10003                 inputblock.cn.push(feedback);
10004             }
10005             
10006             if (this.after) {
10007                 inputblock.cn.push({
10008                     tag :'span',
10009                     cls : 'input-group-addon',
10010                     html : this.after
10011                 });
10012             }
10013             
10014         }
10015         
10016         if (align ==='left' && this.fieldLabel.length) {
10017             cfg.cn = [
10018                 {
10019                     tag: 'label',
10020                     'for' :  id,
10021                     cls : 'control-label',
10022                     html : this.fieldLabel
10023                 },
10024                 {
10025                     cls : "",
10026                     cn: [
10027                         inputblock
10028                     ]
10029                 }
10030
10031             ];
10032             
10033             if(this.labelWidth > 12){
10034                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10035             }
10036
10037             if(this.labelWidth < 13 && this.labelmd == 0){
10038                 this.labelmd = this.labelWidth;
10039             }
10040
10041             if(this.labellg > 0){
10042                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10043                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10044             }
10045
10046             if(this.labelmd > 0){
10047                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10048                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10049             }
10050
10051             if(this.labelsm > 0){
10052                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10053                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10054             }
10055
10056             if(this.labelxs > 0){
10057                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10058                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10059             }
10060             
10061         } else if ( this.fieldLabel.length) {
10062             cfg.cn = [
10063
10064                {
10065                    tag: 'label',
10066                    //cls : 'input-group-addon',
10067                    html : this.fieldLabel
10068
10069                },
10070
10071                inputblock
10072
10073            ];
10074
10075         } else {
10076
10077             cfg.cn = [
10078
10079                 inputblock
10080
10081             ];
10082                 
10083         }
10084         
10085         if (this.disabled) {
10086             input.disabled=true;
10087         }
10088         
10089         return cfg;
10090         
10091     },
10092     /**
10093      * return the real textarea element.
10094      */
10095     inputEl: function ()
10096     {
10097         return this.el.select('textarea.form-control',true).first();
10098     },
10099     
10100     /**
10101      * Clear any invalid styles/messages for this field
10102      */
10103     clearInvalid : function()
10104     {
10105         
10106         if(!this.el || this.preventMark){ // not rendered
10107             return;
10108         }
10109         
10110         var label = this.el.select('label', true).first();
10111         var icon = this.el.select('i.fa-star', true).first();
10112         
10113         if(label && icon){
10114             icon.remove();
10115         }
10116         
10117         this.el.removeClass(this.invalidClass);
10118         
10119         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10120             
10121             var feedback = this.el.select('.form-control-feedback', true).first();
10122             
10123             if(feedback){
10124                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10125             }
10126             
10127         }
10128         
10129         this.fireEvent('valid', this);
10130     },
10131     
10132      /**
10133      * Mark this field as valid
10134      */
10135     markValid : function()
10136     {
10137         if(!this.el  || this.preventMark){ // not rendered
10138             return;
10139         }
10140         
10141         this.el.removeClass([this.invalidClass, this.validClass]);
10142         
10143         var feedback = this.el.select('.form-control-feedback', true).first();
10144             
10145         if(feedback){
10146             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10147         }
10148
10149         if(this.disabled || this.allowBlank){
10150             return;
10151         }
10152         
10153         var label = this.el.select('label', true).first();
10154         var icon = this.el.select('i.fa-star', true).first();
10155         
10156         if(label && icon){
10157             icon.remove();
10158         }
10159         
10160         this.el.addClass(this.validClass);
10161         
10162         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10163             
10164             var feedback = this.el.select('.form-control-feedback', true).first();
10165             
10166             if(feedback){
10167                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10168                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10169             }
10170             
10171         }
10172         
10173         this.fireEvent('valid', this);
10174     },
10175     
10176      /**
10177      * Mark this field as invalid
10178      * @param {String} msg The validation message
10179      */
10180     markInvalid : function(msg)
10181     {
10182         if(!this.el  || this.preventMark){ // not rendered
10183             return;
10184         }
10185         
10186         this.el.removeClass([this.invalidClass, this.validClass]);
10187         
10188         var feedback = this.el.select('.form-control-feedback', true).first();
10189             
10190         if(feedback){
10191             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10192         }
10193
10194         if(this.disabled || this.allowBlank){
10195             return;
10196         }
10197         
10198         var label = this.el.select('label', true).first();
10199         var icon = this.el.select('i.fa-star', true).first();
10200         
10201         if(!this.getValue().length && label && !icon){
10202             this.el.createChild({
10203                 tag : 'i',
10204                 cls : 'text-danger fa fa-lg fa-star',
10205                 tooltip : 'This field is required',
10206                 style : 'margin-right:5px;'
10207             }, label, true);
10208         }
10209
10210         this.el.addClass(this.invalidClass);
10211         
10212         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10213             
10214             var feedback = this.el.select('.form-control-feedback', true).first();
10215             
10216             if(feedback){
10217                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10218                 
10219                 if(this.getValue().length || this.forceFeedback){
10220                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10221                 }
10222                 
10223             }
10224             
10225         }
10226         
10227         this.fireEvent('invalid', this, msg);
10228     }
10229 });
10230
10231  
10232 /*
10233  * - LGPL
10234  *
10235  * trigger field - base class for combo..
10236  * 
10237  */
10238  
10239 /**
10240  * @class Roo.bootstrap.TriggerField
10241  * @extends Roo.bootstrap.Input
10242  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10243  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10244  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10245  * for which you can provide a custom implementation.  For example:
10246  * <pre><code>
10247 var trigger = new Roo.bootstrap.TriggerField();
10248 trigger.onTriggerClick = myTriggerFn;
10249 trigger.applyTo('my-field');
10250 </code></pre>
10251  *
10252  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10253  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10254  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10255  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10256  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10257
10258  * @constructor
10259  * Create a new TriggerField.
10260  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10261  * to the base TextField)
10262  */
10263 Roo.bootstrap.TriggerField = function(config){
10264     this.mimicing = false;
10265     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10266 };
10267
10268 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10269     /**
10270      * @cfg {String} triggerClass A CSS class to apply to the trigger
10271      */
10272      /**
10273      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10274      */
10275     hideTrigger:false,
10276
10277     /**
10278      * @cfg {Boolean} removable (true|false) special filter default false
10279      */
10280     removable : false,
10281     
10282     /** @cfg {Boolean} grow @hide */
10283     /** @cfg {Number} growMin @hide */
10284     /** @cfg {Number} growMax @hide */
10285
10286     /**
10287      * @hide 
10288      * @method
10289      */
10290     autoSize: Roo.emptyFn,
10291     // private
10292     monitorTab : true,
10293     // private
10294     deferHeight : true,
10295
10296     
10297     actionMode : 'wrap',
10298     
10299     caret : false,
10300     
10301     
10302     getAutoCreate : function(){
10303        
10304         var align = this.labelAlign || this.parentLabelAlign();
10305         
10306         var id = Roo.id();
10307         
10308         var cfg = {
10309             cls: 'form-group' //input-group
10310         };
10311         
10312         
10313         var input =  {
10314             tag: 'input',
10315             id : id,
10316             type : this.inputType,
10317             cls : 'form-control',
10318             autocomplete: 'new-password',
10319             placeholder : this.placeholder || '' 
10320             
10321         };
10322         if (this.name) {
10323             input.name = this.name;
10324         }
10325         if (this.size) {
10326             input.cls += ' input-' + this.size;
10327         }
10328         
10329         if (this.disabled) {
10330             input.disabled=true;
10331         }
10332         
10333         var inputblock = input;
10334         
10335         if(this.hasFeedback && !this.allowBlank){
10336             
10337             var feedback = {
10338                 tag: 'span',
10339                 cls: 'glyphicon form-control-feedback'
10340             };
10341             
10342             if(this.removable && !this.editable && !this.tickable){
10343                 inputblock = {
10344                     cls : 'has-feedback',
10345                     cn :  [
10346                         inputblock,
10347                         {
10348                             tag: 'button',
10349                             html : 'x',
10350                             cls : 'roo-combo-removable-btn close'
10351                         },
10352                         feedback
10353                     ] 
10354                 };
10355             } else {
10356                 inputblock = {
10357                     cls : 'has-feedback',
10358                     cn :  [
10359                         inputblock,
10360                         feedback
10361                     ] 
10362                 };
10363             }
10364
10365         } else {
10366             if(this.removable && !this.editable && !this.tickable){
10367                 inputblock = {
10368                     cls : 'roo-removable',
10369                     cn :  [
10370                         inputblock,
10371                         {
10372                             tag: 'button',
10373                             html : 'x',
10374                             cls : 'roo-combo-removable-btn close'
10375                         }
10376                     ] 
10377                 };
10378             }
10379         }
10380         
10381         if (this.before || this.after) {
10382             
10383             inputblock = {
10384                 cls : 'input-group',
10385                 cn :  [] 
10386             };
10387             if (this.before) {
10388                 inputblock.cn.push({
10389                     tag :'span',
10390                     cls : 'input-group-addon input-group-prepend input-group-text',
10391                     html : this.before
10392                 });
10393             }
10394             
10395             inputblock.cn.push(input);
10396             
10397             if(this.hasFeedback && !this.allowBlank){
10398                 inputblock.cls += ' has-feedback';
10399                 inputblock.cn.push(feedback);
10400             }
10401             
10402             if (this.after) {
10403                 inputblock.cn.push({
10404                     tag :'span',
10405                     cls : 'input-group-addon input-group-append input-group-text',
10406                     html : this.after
10407                 });
10408             }
10409             
10410         };
10411         
10412       
10413         
10414         var ibwrap = inputblock;
10415         
10416         if(this.multiple){
10417             ibwrap = {
10418                 tag: 'ul',
10419                 cls: 'roo-select2-choices',
10420                 cn:[
10421                     {
10422                         tag: 'li',
10423                         cls: 'roo-select2-search-field',
10424                         cn: [
10425
10426                             inputblock
10427                         ]
10428                     }
10429                 ]
10430             };
10431                 
10432         }
10433         
10434         var combobox = {
10435             cls: 'roo-select2-container input-group',
10436             cn: [
10437                  {
10438                     tag: 'input',
10439                     type : 'hidden',
10440                     cls: 'form-hidden-field'
10441                 },
10442                 ibwrap
10443             ]
10444         };
10445         
10446         if(!this.multiple && this.showToggleBtn){
10447             
10448             var caret = {
10449                         tag: 'span',
10450                         cls: 'caret'
10451              };
10452             if (this.caret != false) {
10453                 caret = {
10454                      tag: 'i',
10455                      cls: 'fa fa-' + this.caret
10456                 };
10457                 
10458             }
10459             
10460             combobox.cn.push({
10461                 tag :'span',
10462                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10463                 cn : [
10464                     caret,
10465                     {
10466                         tag: 'span',
10467                         cls: 'combobox-clear',
10468                         cn  : [
10469                             {
10470                                 tag : 'i',
10471                                 cls: 'icon-remove'
10472                             }
10473                         ]
10474                     }
10475                 ]
10476
10477             })
10478         }
10479         
10480         if(this.multiple){
10481             combobox.cls += ' roo-select2-container-multi';
10482         }
10483          var indicator = {
10484             tag : 'i',
10485             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10486             tooltip : 'This field is required'
10487         };
10488         if (Roo.bootstrap.version == 4) {
10489             indicator = {
10490                 tag : 'i',
10491                 style : 'display:none'
10492             };
10493         }
10494         
10495         
10496         if (align ==='left' && this.fieldLabel.length) {
10497             
10498             cfg.cls += ' roo-form-group-label-left row';
10499
10500             cfg.cn = [
10501                 indicator,
10502                 {
10503                     tag: 'label',
10504                     'for' :  id,
10505                     cls : 'control-label',
10506                     html : this.fieldLabel
10507
10508                 },
10509                 {
10510                     cls : "", 
10511                     cn: [
10512                         combobox
10513                     ]
10514                 }
10515
10516             ];
10517             
10518             var labelCfg = cfg.cn[1];
10519             var contentCfg = cfg.cn[2];
10520             
10521             if(this.indicatorpos == 'right'){
10522                 cfg.cn = [
10523                     {
10524                         tag: 'label',
10525                         'for' :  id,
10526                         cls : 'control-label',
10527                         cn : [
10528                             {
10529                                 tag : 'span',
10530                                 html : this.fieldLabel
10531                             },
10532                             indicator
10533                         ]
10534                     },
10535                     {
10536                         cls : "", 
10537                         cn: [
10538                             combobox
10539                         ]
10540                     }
10541
10542                 ];
10543                 
10544                 labelCfg = cfg.cn[0];
10545                 contentCfg = cfg.cn[1];
10546             }
10547             
10548             if(this.labelWidth > 12){
10549                 labelCfg.style = "width: " + this.labelWidth + 'px';
10550             }
10551             
10552             if(this.labelWidth < 13 && this.labelmd == 0){
10553                 this.labelmd = this.labelWidth;
10554             }
10555             
10556             if(this.labellg > 0){
10557                 labelCfg.cls += ' col-lg-' + this.labellg;
10558                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10559             }
10560             
10561             if(this.labelmd > 0){
10562                 labelCfg.cls += ' col-md-' + this.labelmd;
10563                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10564             }
10565             
10566             if(this.labelsm > 0){
10567                 labelCfg.cls += ' col-sm-' + this.labelsm;
10568                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10569             }
10570             
10571             if(this.labelxs > 0){
10572                 labelCfg.cls += ' col-xs-' + this.labelxs;
10573                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10574             }
10575             
10576         } else if ( this.fieldLabel.length) {
10577 //                Roo.log(" label");
10578             cfg.cn = [
10579                 indicator,
10580                {
10581                    tag: 'label',
10582                    //cls : 'input-group-addon',
10583                    html : this.fieldLabel
10584
10585                },
10586
10587                combobox
10588
10589             ];
10590             
10591             if(this.indicatorpos == 'right'){
10592                 
10593                 cfg.cn = [
10594                     {
10595                        tag: 'label',
10596                        cn : [
10597                            {
10598                                tag : 'span',
10599                                html : this.fieldLabel
10600                            },
10601                            indicator
10602                        ]
10603
10604                     },
10605                     combobox
10606
10607                 ];
10608
10609             }
10610
10611         } else {
10612             
10613 //                Roo.log(" no label && no align");
10614                 cfg = combobox
10615                      
10616                 
10617         }
10618         
10619         var settings=this;
10620         ['xs','sm','md','lg'].map(function(size){
10621             if (settings[size]) {
10622                 cfg.cls += ' col-' + size + '-' + settings[size];
10623             }
10624         });
10625         
10626         return cfg;
10627         
10628     },
10629     
10630     
10631     
10632     // private
10633     onResize : function(w, h){
10634 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10635 //        if(typeof w == 'number'){
10636 //            var x = w - this.trigger.getWidth();
10637 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10638 //            this.trigger.setStyle('left', x+'px');
10639 //        }
10640     },
10641
10642     // private
10643     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10644
10645     // private
10646     getResizeEl : function(){
10647         return this.inputEl();
10648     },
10649
10650     // private
10651     getPositionEl : function(){
10652         return this.inputEl();
10653     },
10654
10655     // private
10656     alignErrorIcon : function(){
10657         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10658     },
10659
10660     // private
10661     initEvents : function(){
10662         
10663         this.createList();
10664         
10665         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10666         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10667         if(!this.multiple && this.showToggleBtn){
10668             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10669             if(this.hideTrigger){
10670                 this.trigger.setDisplayed(false);
10671             }
10672             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10673         }
10674         
10675         if(this.multiple){
10676             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10677         }
10678         
10679         if(this.removable && !this.editable && !this.tickable){
10680             var close = this.closeTriggerEl();
10681             
10682             if(close){
10683                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10684                 close.on('click', this.removeBtnClick, this, close);
10685             }
10686         }
10687         
10688         //this.trigger.addClassOnOver('x-form-trigger-over');
10689         //this.trigger.addClassOnClick('x-form-trigger-click');
10690         
10691         //if(!this.width){
10692         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10693         //}
10694     },
10695     
10696     closeTriggerEl : function()
10697     {
10698         var close = this.el.select('.roo-combo-removable-btn', true).first();
10699         return close ? close : false;
10700     },
10701     
10702     removeBtnClick : function(e, h, el)
10703     {
10704         e.preventDefault();
10705         
10706         if(this.fireEvent("remove", this) !== false){
10707             this.reset();
10708             this.fireEvent("afterremove", this)
10709         }
10710     },
10711     
10712     createList : function()
10713     {
10714         this.list = Roo.get(document.body).createChild({
10715             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10716             cls: 'typeahead typeahead-long dropdown-menu',
10717             style: 'display:none'
10718         });
10719         
10720         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10721         
10722     },
10723
10724     // private
10725     initTrigger : function(){
10726        
10727     },
10728
10729     // private
10730     onDestroy : function(){
10731         if(this.trigger){
10732             this.trigger.removeAllListeners();
10733           //  this.trigger.remove();
10734         }
10735         //if(this.wrap){
10736         //    this.wrap.remove();
10737         //}
10738         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10739     },
10740
10741     // private
10742     onFocus : function(){
10743         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10744         /*
10745         if(!this.mimicing){
10746             this.wrap.addClass('x-trigger-wrap-focus');
10747             this.mimicing = true;
10748             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10749             if(this.monitorTab){
10750                 this.el.on("keydown", this.checkTab, this);
10751             }
10752         }
10753         */
10754     },
10755
10756     // private
10757     checkTab : function(e){
10758         if(e.getKey() == e.TAB){
10759             this.triggerBlur();
10760         }
10761     },
10762
10763     // private
10764     onBlur : function(){
10765         // do nothing
10766     },
10767
10768     // private
10769     mimicBlur : function(e, t){
10770         /*
10771         if(!this.wrap.contains(t) && this.validateBlur()){
10772             this.triggerBlur();
10773         }
10774         */
10775     },
10776
10777     // private
10778     triggerBlur : function(){
10779         this.mimicing = false;
10780         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10781         if(this.monitorTab){
10782             this.el.un("keydown", this.checkTab, this);
10783         }
10784         //this.wrap.removeClass('x-trigger-wrap-focus');
10785         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10786     },
10787
10788     // private
10789     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10790     validateBlur : function(e, t){
10791         return true;
10792     },
10793
10794     // private
10795     onDisable : function(){
10796         this.inputEl().dom.disabled = true;
10797         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10798         //if(this.wrap){
10799         //    this.wrap.addClass('x-item-disabled');
10800         //}
10801     },
10802
10803     // private
10804     onEnable : function(){
10805         this.inputEl().dom.disabled = false;
10806         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10807         //if(this.wrap){
10808         //    this.el.removeClass('x-item-disabled');
10809         //}
10810     },
10811
10812     // private
10813     onShow : function(){
10814         var ae = this.getActionEl();
10815         
10816         if(ae){
10817             ae.dom.style.display = '';
10818             ae.dom.style.visibility = 'visible';
10819         }
10820     },
10821
10822     // private
10823     
10824     onHide : function(){
10825         var ae = this.getActionEl();
10826         ae.dom.style.display = 'none';
10827     },
10828
10829     /**
10830      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10831      * by an implementing function.
10832      * @method
10833      * @param {EventObject} e
10834      */
10835     onTriggerClick : Roo.emptyFn
10836 });
10837  /*
10838  * Based on:
10839  * Ext JS Library 1.1.1
10840  * Copyright(c) 2006-2007, Ext JS, LLC.
10841  *
10842  * Originally Released Under LGPL - original licence link has changed is not relivant.
10843  *
10844  * Fork - LGPL
10845  * <script type="text/javascript">
10846  */
10847
10848
10849 /**
10850  * @class Roo.data.SortTypes
10851  * @singleton
10852  * Defines the default sorting (casting?) comparison functions used when sorting data.
10853  */
10854 Roo.data.SortTypes = {
10855     /**
10856      * Default sort that does nothing
10857      * @param {Mixed} s The value being converted
10858      * @return {Mixed} The comparison value
10859      */
10860     none : function(s){
10861         return s;
10862     },
10863     
10864     /**
10865      * The regular expression used to strip tags
10866      * @type {RegExp}
10867      * @property
10868      */
10869     stripTagsRE : /<\/?[^>]+>/gi,
10870     
10871     /**
10872      * Strips all HTML tags to sort on text only
10873      * @param {Mixed} s The value being converted
10874      * @return {String} The comparison value
10875      */
10876     asText : function(s){
10877         return String(s).replace(this.stripTagsRE, "");
10878     },
10879     
10880     /**
10881      * Strips all HTML tags to sort on text only - Case insensitive
10882      * @param {Mixed} s The value being converted
10883      * @return {String} The comparison value
10884      */
10885     asUCText : function(s){
10886         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10887     },
10888     
10889     /**
10890      * Case insensitive string
10891      * @param {Mixed} s The value being converted
10892      * @return {String} The comparison value
10893      */
10894     asUCString : function(s) {
10895         return String(s).toUpperCase();
10896     },
10897     
10898     /**
10899      * Date sorting
10900      * @param {Mixed} s The value being converted
10901      * @return {Number} The comparison value
10902      */
10903     asDate : function(s) {
10904         if(!s){
10905             return 0;
10906         }
10907         if(s instanceof Date){
10908             return s.getTime();
10909         }
10910         return Date.parse(String(s));
10911     },
10912     
10913     /**
10914      * Float sorting
10915      * @param {Mixed} s The value being converted
10916      * @return {Float} The comparison value
10917      */
10918     asFloat : function(s) {
10919         var val = parseFloat(String(s).replace(/,/g, ""));
10920         if(isNaN(val)) {
10921             val = 0;
10922         }
10923         return val;
10924     },
10925     
10926     /**
10927      * Integer sorting
10928      * @param {Mixed} s The value being converted
10929      * @return {Number} The comparison value
10930      */
10931     asInt : function(s) {
10932         var val = parseInt(String(s).replace(/,/g, ""));
10933         if(isNaN(val)) {
10934             val = 0;
10935         }
10936         return val;
10937     }
10938 };/*
10939  * Based on:
10940  * Ext JS Library 1.1.1
10941  * Copyright(c) 2006-2007, Ext JS, LLC.
10942  *
10943  * Originally Released Under LGPL - original licence link has changed is not relivant.
10944  *
10945  * Fork - LGPL
10946  * <script type="text/javascript">
10947  */
10948
10949 /**
10950 * @class Roo.data.Record
10951  * Instances of this class encapsulate both record <em>definition</em> information, and record
10952  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10953  * to access Records cached in an {@link Roo.data.Store} object.<br>
10954  * <p>
10955  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10956  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10957  * objects.<br>
10958  * <p>
10959  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10960  * @constructor
10961  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10962  * {@link #create}. The parameters are the same.
10963  * @param {Array} data An associative Array of data values keyed by the field name.
10964  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10965  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10966  * not specified an integer id is generated.
10967  */
10968 Roo.data.Record = function(data, id){
10969     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10970     this.data = data;
10971 };
10972
10973 /**
10974  * Generate a constructor for a specific record layout.
10975  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10976  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10977  * Each field definition object may contain the following properties: <ul>
10978  * <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,
10979  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10980  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10981  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10982  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10983  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10984  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10985  * this may be omitted.</p></li>
10986  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10987  * <ul><li>auto (Default, implies no conversion)</li>
10988  * <li>string</li>
10989  * <li>int</li>
10990  * <li>float</li>
10991  * <li>boolean</li>
10992  * <li>date</li></ul></p></li>
10993  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10994  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10995  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10996  * by the Reader into an object that will be stored in the Record. It is passed the
10997  * following parameters:<ul>
10998  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10999  * </ul></p></li>
11000  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11001  * </ul>
11002  * <br>usage:<br><pre><code>
11003 var TopicRecord = Roo.data.Record.create(
11004     {name: 'title', mapping: 'topic_title'},
11005     {name: 'author', mapping: 'username'},
11006     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11007     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11008     {name: 'lastPoster', mapping: 'user2'},
11009     {name: 'excerpt', mapping: 'post_text'}
11010 );
11011
11012 var myNewRecord = new TopicRecord({
11013     title: 'Do my job please',
11014     author: 'noobie',
11015     totalPosts: 1,
11016     lastPost: new Date(),
11017     lastPoster: 'Animal',
11018     excerpt: 'No way dude!'
11019 });
11020 myStore.add(myNewRecord);
11021 </code></pre>
11022  * @method create
11023  * @static
11024  */
11025 Roo.data.Record.create = function(o){
11026     var f = function(){
11027         f.superclass.constructor.apply(this, arguments);
11028     };
11029     Roo.extend(f, Roo.data.Record);
11030     var p = f.prototype;
11031     p.fields = new Roo.util.MixedCollection(false, function(field){
11032         return field.name;
11033     });
11034     for(var i = 0, len = o.length; i < len; i++){
11035         p.fields.add(new Roo.data.Field(o[i]));
11036     }
11037     f.getField = function(name){
11038         return p.fields.get(name);  
11039     };
11040     return f;
11041 };
11042
11043 Roo.data.Record.AUTO_ID = 1000;
11044 Roo.data.Record.EDIT = 'edit';
11045 Roo.data.Record.REJECT = 'reject';
11046 Roo.data.Record.COMMIT = 'commit';
11047
11048 Roo.data.Record.prototype = {
11049     /**
11050      * Readonly flag - true if this record has been modified.
11051      * @type Boolean
11052      */
11053     dirty : false,
11054     editing : false,
11055     error: null,
11056     modified: null,
11057
11058     // private
11059     join : function(store){
11060         this.store = store;
11061     },
11062
11063     /**
11064      * Set the named field to the specified value.
11065      * @param {String} name The name of the field to set.
11066      * @param {Object} value The value to set the field to.
11067      */
11068     set : function(name, value){
11069         if(this.data[name] == value){
11070             return;
11071         }
11072         this.dirty = true;
11073         if(!this.modified){
11074             this.modified = {};
11075         }
11076         if(typeof this.modified[name] == 'undefined'){
11077             this.modified[name] = this.data[name];
11078         }
11079         this.data[name] = value;
11080         if(!this.editing && this.store){
11081             this.store.afterEdit(this);
11082         }       
11083     },
11084
11085     /**
11086      * Get the value of the named field.
11087      * @param {String} name The name of the field to get the value of.
11088      * @return {Object} The value of the field.
11089      */
11090     get : function(name){
11091         return this.data[name]; 
11092     },
11093
11094     // private
11095     beginEdit : function(){
11096         this.editing = true;
11097         this.modified = {}; 
11098     },
11099
11100     // private
11101     cancelEdit : function(){
11102         this.editing = false;
11103         delete this.modified;
11104     },
11105
11106     // private
11107     endEdit : function(){
11108         this.editing = false;
11109         if(this.dirty && this.store){
11110             this.store.afterEdit(this);
11111         }
11112     },
11113
11114     /**
11115      * Usually called by the {@link Roo.data.Store} which owns the Record.
11116      * Rejects all changes made to the Record since either creation, or the last commit operation.
11117      * Modified fields are reverted to their original values.
11118      * <p>
11119      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11120      * of reject operations.
11121      */
11122     reject : function(){
11123         var m = this.modified;
11124         for(var n in m){
11125             if(typeof m[n] != "function"){
11126                 this.data[n] = m[n];
11127             }
11128         }
11129         this.dirty = false;
11130         delete this.modified;
11131         this.editing = false;
11132         if(this.store){
11133             this.store.afterReject(this);
11134         }
11135     },
11136
11137     /**
11138      * Usually called by the {@link Roo.data.Store} which owns the Record.
11139      * Commits all changes made to the Record since either creation, or the last commit operation.
11140      * <p>
11141      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11142      * of commit operations.
11143      */
11144     commit : function(){
11145         this.dirty = false;
11146         delete this.modified;
11147         this.editing = false;
11148         if(this.store){
11149             this.store.afterCommit(this);
11150         }
11151     },
11152
11153     // private
11154     hasError : function(){
11155         return this.error != null;
11156     },
11157
11158     // private
11159     clearError : function(){
11160         this.error = null;
11161     },
11162
11163     /**
11164      * Creates a copy of this record.
11165      * @param {String} id (optional) A new record id if you don't want to use this record's id
11166      * @return {Record}
11167      */
11168     copy : function(newId) {
11169         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11170     }
11171 };/*
11172  * Based on:
11173  * Ext JS Library 1.1.1
11174  * Copyright(c) 2006-2007, Ext JS, LLC.
11175  *
11176  * Originally Released Under LGPL - original licence link has changed is not relivant.
11177  *
11178  * Fork - LGPL
11179  * <script type="text/javascript">
11180  */
11181
11182
11183
11184 /**
11185  * @class Roo.data.Store
11186  * @extends Roo.util.Observable
11187  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11188  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11189  * <p>
11190  * 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
11191  * has no knowledge of the format of the data returned by the Proxy.<br>
11192  * <p>
11193  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11194  * instances from the data object. These records are cached and made available through accessor functions.
11195  * @constructor
11196  * Creates a new Store.
11197  * @param {Object} config A config object containing the objects needed for the Store to access data,
11198  * and read the data into Records.
11199  */
11200 Roo.data.Store = function(config){
11201     this.data = new Roo.util.MixedCollection(false);
11202     this.data.getKey = function(o){
11203         return o.id;
11204     };
11205     this.baseParams = {};
11206     // private
11207     this.paramNames = {
11208         "start" : "start",
11209         "limit" : "limit",
11210         "sort" : "sort",
11211         "dir" : "dir",
11212         "multisort" : "_multisort"
11213     };
11214
11215     if(config && config.data){
11216         this.inlineData = config.data;
11217         delete config.data;
11218     }
11219
11220     Roo.apply(this, config);
11221     
11222     if(this.reader){ // reader passed
11223         this.reader = Roo.factory(this.reader, Roo.data);
11224         this.reader.xmodule = this.xmodule || false;
11225         if(!this.recordType){
11226             this.recordType = this.reader.recordType;
11227         }
11228         if(this.reader.onMetaChange){
11229             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11230         }
11231     }
11232
11233     if(this.recordType){
11234         this.fields = this.recordType.prototype.fields;
11235     }
11236     this.modified = [];
11237
11238     this.addEvents({
11239         /**
11240          * @event datachanged
11241          * Fires when the data cache has changed, and a widget which is using this Store
11242          * as a Record cache should refresh its view.
11243          * @param {Store} this
11244          */
11245         datachanged : true,
11246         /**
11247          * @event metachange
11248          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11249          * @param {Store} this
11250          * @param {Object} meta The JSON metadata
11251          */
11252         metachange : true,
11253         /**
11254          * @event add
11255          * Fires when Records have been added to the Store
11256          * @param {Store} this
11257          * @param {Roo.data.Record[]} records The array of Records added
11258          * @param {Number} index The index at which the record(s) were added
11259          */
11260         add : true,
11261         /**
11262          * @event remove
11263          * Fires when a Record has been removed from the Store
11264          * @param {Store} this
11265          * @param {Roo.data.Record} record The Record that was removed
11266          * @param {Number} index The index at which the record was removed
11267          */
11268         remove : true,
11269         /**
11270          * @event update
11271          * Fires when a Record has been updated
11272          * @param {Store} this
11273          * @param {Roo.data.Record} record The Record that was updated
11274          * @param {String} operation The update operation being performed.  Value may be one of:
11275          * <pre><code>
11276  Roo.data.Record.EDIT
11277  Roo.data.Record.REJECT
11278  Roo.data.Record.COMMIT
11279          * </code></pre>
11280          */
11281         update : true,
11282         /**
11283          * @event clear
11284          * Fires when the data cache has been cleared.
11285          * @param {Store} this
11286          */
11287         clear : true,
11288         /**
11289          * @event beforeload
11290          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11291          * the load action will be canceled.
11292          * @param {Store} this
11293          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11294          */
11295         beforeload : true,
11296         /**
11297          * @event beforeloadadd
11298          * Fires after a new set of Records has been loaded.
11299          * @param {Store} this
11300          * @param {Roo.data.Record[]} records The Records that were loaded
11301          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11302          */
11303         beforeloadadd : true,
11304         /**
11305          * @event load
11306          * Fires after a new set of Records has been loaded, before they are added to the store.
11307          * @param {Store} this
11308          * @param {Roo.data.Record[]} records The Records that were loaded
11309          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11310          * @params {Object} return from reader
11311          */
11312         load : true,
11313         /**
11314          * @event loadexception
11315          * Fires if an exception occurs in the Proxy during loading.
11316          * Called with the signature of the Proxy's "loadexception" event.
11317          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11318          * 
11319          * @param {Proxy} 
11320          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11321          * @param {Object} load options 
11322          * @param {Object} jsonData from your request (normally this contains the Exception)
11323          */
11324         loadexception : true
11325     });
11326     
11327     if(this.proxy){
11328         this.proxy = Roo.factory(this.proxy, Roo.data);
11329         this.proxy.xmodule = this.xmodule || false;
11330         this.relayEvents(this.proxy,  ["loadexception"]);
11331     }
11332     this.sortToggle = {};
11333     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11334
11335     Roo.data.Store.superclass.constructor.call(this);
11336
11337     if(this.inlineData){
11338         this.loadData(this.inlineData);
11339         delete this.inlineData;
11340     }
11341 };
11342
11343 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11344      /**
11345     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11346     * without a remote query - used by combo/forms at present.
11347     */
11348     
11349     /**
11350     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11351     */
11352     /**
11353     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11354     */
11355     /**
11356     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11357     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11358     */
11359     /**
11360     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11361     * on any HTTP request
11362     */
11363     /**
11364     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11365     */
11366     /**
11367     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11368     */
11369     multiSort: false,
11370     /**
11371     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11372     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11373     */
11374     remoteSort : false,
11375
11376     /**
11377     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11378      * loaded or when a record is removed. (defaults to false).
11379     */
11380     pruneModifiedRecords : false,
11381
11382     // private
11383     lastOptions : null,
11384
11385     /**
11386      * Add Records to the Store and fires the add event.
11387      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11388      */
11389     add : function(records){
11390         records = [].concat(records);
11391         for(var i = 0, len = records.length; i < len; i++){
11392             records[i].join(this);
11393         }
11394         var index = this.data.length;
11395         this.data.addAll(records);
11396         this.fireEvent("add", this, records, index);
11397     },
11398
11399     /**
11400      * Remove a Record from the Store and fires the remove event.
11401      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11402      */
11403     remove : function(record){
11404         var index = this.data.indexOf(record);
11405         this.data.removeAt(index);
11406  
11407         if(this.pruneModifiedRecords){
11408             this.modified.remove(record);
11409         }
11410         this.fireEvent("remove", this, record, index);
11411     },
11412
11413     /**
11414      * Remove all Records from the Store and fires the clear event.
11415      */
11416     removeAll : function(){
11417         this.data.clear();
11418         if(this.pruneModifiedRecords){
11419             this.modified = [];
11420         }
11421         this.fireEvent("clear", this);
11422     },
11423
11424     /**
11425      * Inserts Records to the Store at the given index and fires the add event.
11426      * @param {Number} index The start index at which to insert the passed Records.
11427      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11428      */
11429     insert : function(index, records){
11430         records = [].concat(records);
11431         for(var i = 0, len = records.length; i < len; i++){
11432             this.data.insert(index, records[i]);
11433             records[i].join(this);
11434         }
11435         this.fireEvent("add", this, records, index);
11436     },
11437
11438     /**
11439      * Get the index within the cache of the passed Record.
11440      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11441      * @return {Number} The index of the passed Record. Returns -1 if not found.
11442      */
11443     indexOf : function(record){
11444         return this.data.indexOf(record);
11445     },
11446
11447     /**
11448      * Get the index within the cache of the Record with the passed id.
11449      * @param {String} id The id of the Record to find.
11450      * @return {Number} The index of the Record. Returns -1 if not found.
11451      */
11452     indexOfId : function(id){
11453         return this.data.indexOfKey(id);
11454     },
11455
11456     /**
11457      * Get the Record with the specified id.
11458      * @param {String} id The id of the Record to find.
11459      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11460      */
11461     getById : function(id){
11462         return this.data.key(id);
11463     },
11464
11465     /**
11466      * Get the Record at the specified index.
11467      * @param {Number} index The index of the Record to find.
11468      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11469      */
11470     getAt : function(index){
11471         return this.data.itemAt(index);
11472     },
11473
11474     /**
11475      * Returns a range of Records between specified indices.
11476      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11477      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11478      * @return {Roo.data.Record[]} An array of Records
11479      */
11480     getRange : function(start, end){
11481         return this.data.getRange(start, end);
11482     },
11483
11484     // private
11485     storeOptions : function(o){
11486         o = Roo.apply({}, o);
11487         delete o.callback;
11488         delete o.scope;
11489         this.lastOptions = o;
11490     },
11491
11492     /**
11493      * Loads the Record cache from the configured Proxy using the configured Reader.
11494      * <p>
11495      * If using remote paging, then the first load call must specify the <em>start</em>
11496      * and <em>limit</em> properties in the options.params property to establish the initial
11497      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11498      * <p>
11499      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11500      * and this call will return before the new data has been loaded. Perform any post-processing
11501      * in a callback function, or in a "load" event handler.</strong>
11502      * <p>
11503      * @param {Object} options An object containing properties which control loading options:<ul>
11504      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11505      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11506      * passed the following arguments:<ul>
11507      * <li>r : Roo.data.Record[]</li>
11508      * <li>options: Options object from the load call</li>
11509      * <li>success: Boolean success indicator</li></ul></li>
11510      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11511      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11512      * </ul>
11513      */
11514     load : function(options){
11515         options = options || {};
11516         if(this.fireEvent("beforeload", this, options) !== false){
11517             this.storeOptions(options);
11518             var p = Roo.apply(options.params || {}, this.baseParams);
11519             // if meta was not loaded from remote source.. try requesting it.
11520             if (!this.reader.metaFromRemote) {
11521                 p._requestMeta = 1;
11522             }
11523             if(this.sortInfo && this.remoteSort){
11524                 var pn = this.paramNames;
11525                 p[pn["sort"]] = this.sortInfo.field;
11526                 p[pn["dir"]] = this.sortInfo.direction;
11527             }
11528             if (this.multiSort) {
11529                 var pn = this.paramNames;
11530                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11531             }
11532             
11533             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11534         }
11535     },
11536
11537     /**
11538      * Reloads the Record cache from the configured Proxy using the configured Reader and
11539      * the options from the last load operation performed.
11540      * @param {Object} options (optional) An object containing properties which may override the options
11541      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11542      * the most recently used options are reused).
11543      */
11544     reload : function(options){
11545         this.load(Roo.applyIf(options||{}, this.lastOptions));
11546     },
11547
11548     // private
11549     // Called as a callback by the Reader during a load operation.
11550     loadRecords : function(o, options, success){
11551         if(!o || success === false){
11552             if(success !== false){
11553                 this.fireEvent("load", this, [], options, o);
11554             }
11555             if(options.callback){
11556                 options.callback.call(options.scope || this, [], options, false);
11557             }
11558             return;
11559         }
11560         // if data returned failure - throw an exception.
11561         if (o.success === false) {
11562             // show a message if no listener is registered.
11563             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11564                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11565             }
11566             // loadmask wil be hooked into this..
11567             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11568             return;
11569         }
11570         var r = o.records, t = o.totalRecords || r.length;
11571         
11572         this.fireEvent("beforeloadadd", this, r, options, o);
11573         
11574         if(!options || options.add !== true){
11575             if(this.pruneModifiedRecords){
11576                 this.modified = [];
11577             }
11578             for(var i = 0, len = r.length; i < len; i++){
11579                 r[i].join(this);
11580             }
11581             if(this.snapshot){
11582                 this.data = this.snapshot;
11583                 delete this.snapshot;
11584             }
11585             this.data.clear();
11586             this.data.addAll(r);
11587             this.totalLength = t;
11588             this.applySort();
11589             this.fireEvent("datachanged", this);
11590         }else{
11591             this.totalLength = Math.max(t, this.data.length+r.length);
11592             this.add(r);
11593         }
11594         
11595         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11596                 
11597             var e = new Roo.data.Record({});
11598
11599             e.set(this.parent.displayField, this.parent.emptyTitle);
11600             e.set(this.parent.valueField, '');
11601
11602             this.insert(0, e);
11603         }
11604             
11605         this.fireEvent("load", this, r, options, o);
11606         if(options.callback){
11607             options.callback.call(options.scope || this, r, options, true);
11608         }
11609     },
11610
11611
11612     /**
11613      * Loads data from a passed data block. A Reader which understands the format of the data
11614      * must have been configured in the constructor.
11615      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11616      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11617      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11618      */
11619     loadData : function(o, append){
11620         var r = this.reader.readRecords(o);
11621         this.loadRecords(r, {add: append}, true);
11622     },
11623
11624     /**
11625      * Gets the number of cached records.
11626      * <p>
11627      * <em>If using paging, this may not be the total size of the dataset. If the data object
11628      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11629      * the data set size</em>
11630      */
11631     getCount : function(){
11632         return this.data.length || 0;
11633     },
11634
11635     /**
11636      * Gets the total number of records in the dataset as returned by the server.
11637      * <p>
11638      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11639      * the dataset size</em>
11640      */
11641     getTotalCount : function(){
11642         return this.totalLength || 0;
11643     },
11644
11645     /**
11646      * Returns the sort state of the Store as an object with two properties:
11647      * <pre><code>
11648  field {String} The name of the field by which the Records are sorted
11649  direction {String} The sort order, "ASC" or "DESC"
11650      * </code></pre>
11651      */
11652     getSortState : function(){
11653         return this.sortInfo;
11654     },
11655
11656     // private
11657     applySort : function(){
11658         if(this.sortInfo && !this.remoteSort){
11659             var s = this.sortInfo, f = s.field;
11660             var st = this.fields.get(f).sortType;
11661             var fn = function(r1, r2){
11662                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11663                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11664             };
11665             this.data.sort(s.direction, fn);
11666             if(this.snapshot && this.snapshot != this.data){
11667                 this.snapshot.sort(s.direction, fn);
11668             }
11669         }
11670     },
11671
11672     /**
11673      * Sets the default sort column and order to be used by the next load operation.
11674      * @param {String} fieldName The name of the field to sort by.
11675      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11676      */
11677     setDefaultSort : function(field, dir){
11678         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11679     },
11680
11681     /**
11682      * Sort the Records.
11683      * If remote sorting is used, the sort is performed on the server, and the cache is
11684      * reloaded. If local sorting is used, the cache is sorted internally.
11685      * @param {String} fieldName The name of the field to sort by.
11686      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11687      */
11688     sort : function(fieldName, dir){
11689         var f = this.fields.get(fieldName);
11690         if(!dir){
11691             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11692             
11693             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11694                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11695             }else{
11696                 dir = f.sortDir;
11697             }
11698         }
11699         this.sortToggle[f.name] = dir;
11700         this.sortInfo = {field: f.name, direction: dir};
11701         if(!this.remoteSort){
11702             this.applySort();
11703             this.fireEvent("datachanged", this);
11704         }else{
11705             this.load(this.lastOptions);
11706         }
11707     },
11708
11709     /**
11710      * Calls the specified function for each of the Records in the cache.
11711      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11712      * Returning <em>false</em> aborts and exits the iteration.
11713      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11714      */
11715     each : function(fn, scope){
11716         this.data.each(fn, scope);
11717     },
11718
11719     /**
11720      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11721      * (e.g., during paging).
11722      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11723      */
11724     getModifiedRecords : function(){
11725         return this.modified;
11726     },
11727
11728     // private
11729     createFilterFn : function(property, value, anyMatch){
11730         if(!value.exec){ // not a regex
11731             value = String(value);
11732             if(value.length == 0){
11733                 return false;
11734             }
11735             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11736         }
11737         return function(r){
11738             return value.test(r.data[property]);
11739         };
11740     },
11741
11742     /**
11743      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11744      * @param {String} property A field on your records
11745      * @param {Number} start The record index to start at (defaults to 0)
11746      * @param {Number} end The last record index to include (defaults to length - 1)
11747      * @return {Number} The sum
11748      */
11749     sum : function(property, start, end){
11750         var rs = this.data.items, v = 0;
11751         start = start || 0;
11752         end = (end || end === 0) ? end : rs.length-1;
11753
11754         for(var i = start; i <= end; i++){
11755             v += (rs[i].data[property] || 0);
11756         }
11757         return v;
11758     },
11759
11760     /**
11761      * Filter the records by a specified property.
11762      * @param {String} field A field on your records
11763      * @param {String/RegExp} value Either a string that the field
11764      * should start with or a RegExp to test against the field
11765      * @param {Boolean} anyMatch True to match any part not just the beginning
11766      */
11767     filter : function(property, value, anyMatch){
11768         var fn = this.createFilterFn(property, value, anyMatch);
11769         return fn ? this.filterBy(fn) : this.clearFilter();
11770     },
11771
11772     /**
11773      * Filter by a function. The specified function will be called with each
11774      * record in this data source. If the function returns true the record is included,
11775      * otherwise it is filtered.
11776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11777      * @param {Object} scope (optional) The scope of the function (defaults to this)
11778      */
11779     filterBy : function(fn, scope){
11780         this.snapshot = this.snapshot || this.data;
11781         this.data = this.queryBy(fn, scope||this);
11782         this.fireEvent("datachanged", this);
11783     },
11784
11785     /**
11786      * Query the records by a specified property.
11787      * @param {String} field A field on your records
11788      * @param {String/RegExp} value Either a string that the field
11789      * should start with or a RegExp to test against the field
11790      * @param {Boolean} anyMatch True to match any part not just the beginning
11791      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11792      */
11793     query : function(property, value, anyMatch){
11794         var fn = this.createFilterFn(property, value, anyMatch);
11795         return fn ? this.queryBy(fn) : this.data.clone();
11796     },
11797
11798     /**
11799      * Query by a function. The specified function will be called with each
11800      * record in this data source. If the function returns true the record is included
11801      * in the results.
11802      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11803      * @param {Object} scope (optional) The scope of the function (defaults to this)
11804       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11805      **/
11806     queryBy : function(fn, scope){
11807         var data = this.snapshot || this.data;
11808         return data.filterBy(fn, scope||this);
11809     },
11810
11811     /**
11812      * Collects unique values for a particular dataIndex from this store.
11813      * @param {String} dataIndex The property to collect
11814      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11815      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11816      * @return {Array} An array of the unique values
11817      **/
11818     collect : function(dataIndex, allowNull, bypassFilter){
11819         var d = (bypassFilter === true && this.snapshot) ?
11820                 this.snapshot.items : this.data.items;
11821         var v, sv, r = [], l = {};
11822         for(var i = 0, len = d.length; i < len; i++){
11823             v = d[i].data[dataIndex];
11824             sv = String(v);
11825             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11826                 l[sv] = true;
11827                 r[r.length] = v;
11828             }
11829         }
11830         return r;
11831     },
11832
11833     /**
11834      * Revert to a view of the Record cache with no filtering applied.
11835      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11836      */
11837     clearFilter : function(suppressEvent){
11838         if(this.snapshot && this.snapshot != this.data){
11839             this.data = this.snapshot;
11840             delete this.snapshot;
11841             if(suppressEvent !== true){
11842                 this.fireEvent("datachanged", this);
11843             }
11844         }
11845     },
11846
11847     // private
11848     afterEdit : function(record){
11849         if(this.modified.indexOf(record) == -1){
11850             this.modified.push(record);
11851         }
11852         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11853     },
11854     
11855     // private
11856     afterReject : function(record){
11857         this.modified.remove(record);
11858         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11859     },
11860
11861     // private
11862     afterCommit : function(record){
11863         this.modified.remove(record);
11864         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11865     },
11866
11867     /**
11868      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11869      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11870      */
11871     commitChanges : function(){
11872         var m = this.modified.slice(0);
11873         this.modified = [];
11874         for(var i = 0, len = m.length; i < len; i++){
11875             m[i].commit();
11876         }
11877     },
11878
11879     /**
11880      * Cancel outstanding changes on all changed records.
11881      */
11882     rejectChanges : function(){
11883         var m = this.modified.slice(0);
11884         this.modified = [];
11885         for(var i = 0, len = m.length; i < len; i++){
11886             m[i].reject();
11887         }
11888     },
11889
11890     onMetaChange : function(meta, rtype, o){
11891         this.recordType = rtype;
11892         this.fields = rtype.prototype.fields;
11893         delete this.snapshot;
11894         this.sortInfo = meta.sortInfo || this.sortInfo;
11895         this.modified = [];
11896         this.fireEvent('metachange', this, this.reader.meta);
11897     },
11898     
11899     moveIndex : function(data, type)
11900     {
11901         var index = this.indexOf(data);
11902         
11903         var newIndex = index + type;
11904         
11905         this.remove(data);
11906         
11907         this.insert(newIndex, data);
11908         
11909     }
11910 });/*
11911  * Based on:
11912  * Ext JS Library 1.1.1
11913  * Copyright(c) 2006-2007, Ext JS, LLC.
11914  *
11915  * Originally Released Under LGPL - original licence link has changed is not relivant.
11916  *
11917  * Fork - LGPL
11918  * <script type="text/javascript">
11919  */
11920
11921 /**
11922  * @class Roo.data.SimpleStore
11923  * @extends Roo.data.Store
11924  * Small helper class to make creating Stores from Array data easier.
11925  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11926  * @cfg {Array} fields An array of field definition objects, or field name strings.
11927  * @cfg {Array} data The multi-dimensional array of data
11928  * @constructor
11929  * @param {Object} config
11930  */
11931 Roo.data.SimpleStore = function(config){
11932     Roo.data.SimpleStore.superclass.constructor.call(this, {
11933         isLocal : true,
11934         reader: new Roo.data.ArrayReader({
11935                 id: config.id
11936             },
11937             Roo.data.Record.create(config.fields)
11938         ),
11939         proxy : new Roo.data.MemoryProxy(config.data)
11940     });
11941     this.load();
11942 };
11943 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11944  * Based on:
11945  * Ext JS Library 1.1.1
11946  * Copyright(c) 2006-2007, Ext JS, LLC.
11947  *
11948  * Originally Released Under LGPL - original licence link has changed is not relivant.
11949  *
11950  * Fork - LGPL
11951  * <script type="text/javascript">
11952  */
11953
11954 /**
11955 /**
11956  * @extends Roo.data.Store
11957  * @class Roo.data.JsonStore
11958  * Small helper class to make creating Stores for JSON data easier. <br/>
11959 <pre><code>
11960 var store = new Roo.data.JsonStore({
11961     url: 'get-images.php',
11962     root: 'images',
11963     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11964 });
11965 </code></pre>
11966  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11967  * JsonReader and HttpProxy (unless inline data is provided).</b>
11968  * @cfg {Array} fields An array of field definition objects, or field name strings.
11969  * @constructor
11970  * @param {Object} config
11971  */
11972 Roo.data.JsonStore = function(c){
11973     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11974         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11975         reader: new Roo.data.JsonReader(c, c.fields)
11976     }));
11977 };
11978 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11979  * Based on:
11980  * Ext JS Library 1.1.1
11981  * Copyright(c) 2006-2007, Ext JS, LLC.
11982  *
11983  * Originally Released Under LGPL - original licence link has changed is not relivant.
11984  *
11985  * Fork - LGPL
11986  * <script type="text/javascript">
11987  */
11988
11989  
11990 Roo.data.Field = function(config){
11991     if(typeof config == "string"){
11992         config = {name: config};
11993     }
11994     Roo.apply(this, config);
11995     
11996     if(!this.type){
11997         this.type = "auto";
11998     }
11999     
12000     var st = Roo.data.SortTypes;
12001     // named sortTypes are supported, here we look them up
12002     if(typeof this.sortType == "string"){
12003         this.sortType = st[this.sortType];
12004     }
12005     
12006     // set default sortType for strings and dates
12007     if(!this.sortType){
12008         switch(this.type){
12009             case "string":
12010                 this.sortType = st.asUCString;
12011                 break;
12012             case "date":
12013                 this.sortType = st.asDate;
12014                 break;
12015             default:
12016                 this.sortType = st.none;
12017         }
12018     }
12019
12020     // define once
12021     var stripRe = /[\$,%]/g;
12022
12023     // prebuilt conversion function for this field, instead of
12024     // switching every time we're reading a value
12025     if(!this.convert){
12026         var cv, dateFormat = this.dateFormat;
12027         switch(this.type){
12028             case "":
12029             case "auto":
12030             case undefined:
12031                 cv = function(v){ return v; };
12032                 break;
12033             case "string":
12034                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12035                 break;
12036             case "int":
12037                 cv = function(v){
12038                     return v !== undefined && v !== null && v !== '' ?
12039                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12040                     };
12041                 break;
12042             case "float":
12043                 cv = function(v){
12044                     return v !== undefined && v !== null && v !== '' ?
12045                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12046                     };
12047                 break;
12048             case "bool":
12049             case "boolean":
12050                 cv = function(v){ return v === true || v === "true" || v == 1; };
12051                 break;
12052             case "date":
12053                 cv = function(v){
12054                     if(!v){
12055                         return '';
12056                     }
12057                     if(v instanceof Date){
12058                         return v;
12059                     }
12060                     if(dateFormat){
12061                         if(dateFormat == "timestamp"){
12062                             return new Date(v*1000);
12063                         }
12064                         return Date.parseDate(v, dateFormat);
12065                     }
12066                     var parsed = Date.parse(v);
12067                     return parsed ? new Date(parsed) : null;
12068                 };
12069              break;
12070             
12071         }
12072         this.convert = cv;
12073     }
12074 };
12075
12076 Roo.data.Field.prototype = {
12077     dateFormat: null,
12078     defaultValue: "",
12079     mapping: null,
12080     sortType : null,
12081     sortDir : "ASC"
12082 };/*
12083  * Based on:
12084  * Ext JS Library 1.1.1
12085  * Copyright(c) 2006-2007, Ext JS, LLC.
12086  *
12087  * Originally Released Under LGPL - original licence link has changed is not relivant.
12088  *
12089  * Fork - LGPL
12090  * <script type="text/javascript">
12091  */
12092  
12093 // Base class for reading structured data from a data source.  This class is intended to be
12094 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12095
12096 /**
12097  * @class Roo.data.DataReader
12098  * Base class for reading structured data from a data source.  This class is intended to be
12099  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12100  */
12101
12102 Roo.data.DataReader = function(meta, recordType){
12103     
12104     this.meta = meta;
12105     
12106     this.recordType = recordType instanceof Array ? 
12107         Roo.data.Record.create(recordType) : recordType;
12108 };
12109
12110 Roo.data.DataReader.prototype = {
12111      /**
12112      * Create an empty record
12113      * @param {Object} data (optional) - overlay some values
12114      * @return {Roo.data.Record} record created.
12115      */
12116     newRow :  function(d) {
12117         var da =  {};
12118         this.recordType.prototype.fields.each(function(c) {
12119             switch( c.type) {
12120                 case 'int' : da[c.name] = 0; break;
12121                 case 'date' : da[c.name] = new Date(); break;
12122                 case 'float' : da[c.name] = 0.0; break;
12123                 case 'boolean' : da[c.name] = false; break;
12124                 default : da[c.name] = ""; break;
12125             }
12126             
12127         });
12128         return new this.recordType(Roo.apply(da, d));
12129     }
12130     
12131 };/*
12132  * Based on:
12133  * Ext JS Library 1.1.1
12134  * Copyright(c) 2006-2007, Ext JS, LLC.
12135  *
12136  * Originally Released Under LGPL - original licence link has changed is not relivant.
12137  *
12138  * Fork - LGPL
12139  * <script type="text/javascript">
12140  */
12141
12142 /**
12143  * @class Roo.data.DataProxy
12144  * @extends Roo.data.Observable
12145  * This class is an abstract base class for implementations which provide retrieval of
12146  * unformatted data objects.<br>
12147  * <p>
12148  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12149  * (of the appropriate type which knows how to parse the data object) to provide a block of
12150  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12151  * <p>
12152  * Custom implementations must implement the load method as described in
12153  * {@link Roo.data.HttpProxy#load}.
12154  */
12155 Roo.data.DataProxy = function(){
12156     this.addEvents({
12157         /**
12158          * @event beforeload
12159          * Fires before a network request is made to retrieve a data object.
12160          * @param {Object} This DataProxy object.
12161          * @param {Object} params The params parameter to the load function.
12162          */
12163         beforeload : true,
12164         /**
12165          * @event load
12166          * Fires before the load method's callback is called.
12167          * @param {Object} This DataProxy object.
12168          * @param {Object} o The data object.
12169          * @param {Object} arg The callback argument object passed to the load function.
12170          */
12171         load : true,
12172         /**
12173          * @event loadexception
12174          * Fires if an Exception occurs during data retrieval.
12175          * @param {Object} This DataProxy object.
12176          * @param {Object} o The data object.
12177          * @param {Object} arg The callback argument object passed to the load function.
12178          * @param {Object} e The Exception.
12179          */
12180         loadexception : true
12181     });
12182     Roo.data.DataProxy.superclass.constructor.call(this);
12183 };
12184
12185 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12186
12187     /**
12188      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12189      */
12190 /*
12191  * Based on:
12192  * Ext JS Library 1.1.1
12193  * Copyright(c) 2006-2007, Ext JS, LLC.
12194  *
12195  * Originally Released Under LGPL - original licence link has changed is not relivant.
12196  *
12197  * Fork - LGPL
12198  * <script type="text/javascript">
12199  */
12200 /**
12201  * @class Roo.data.MemoryProxy
12202  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12203  * to the Reader when its load method is called.
12204  * @constructor
12205  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12206  */
12207 Roo.data.MemoryProxy = function(data){
12208     if (data.data) {
12209         data = data.data;
12210     }
12211     Roo.data.MemoryProxy.superclass.constructor.call(this);
12212     this.data = data;
12213 };
12214
12215 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12216     
12217     /**
12218      * Load data from the requested source (in this case an in-memory
12219      * data object passed to the constructor), read the data object into
12220      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12221      * process that block using the passed callback.
12222      * @param {Object} params This parameter is not used by the MemoryProxy class.
12223      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12224      * object into a block of Roo.data.Records.
12225      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12226      * The function must be passed <ul>
12227      * <li>The Record block object</li>
12228      * <li>The "arg" argument from the load function</li>
12229      * <li>A boolean success indicator</li>
12230      * </ul>
12231      * @param {Object} scope The scope in which to call the callback
12232      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12233      */
12234     load : function(params, reader, callback, scope, arg){
12235         params = params || {};
12236         var result;
12237         try {
12238             result = reader.readRecords(this.data);
12239         }catch(e){
12240             this.fireEvent("loadexception", this, arg, null, e);
12241             callback.call(scope, null, arg, false);
12242             return;
12243         }
12244         callback.call(scope, result, arg, true);
12245     },
12246     
12247     // private
12248     update : function(params, records){
12249         
12250     }
12251 });/*
12252  * Based on:
12253  * Ext JS Library 1.1.1
12254  * Copyright(c) 2006-2007, Ext JS, LLC.
12255  *
12256  * Originally Released Under LGPL - original licence link has changed is not relivant.
12257  *
12258  * Fork - LGPL
12259  * <script type="text/javascript">
12260  */
12261 /**
12262  * @class Roo.data.HttpProxy
12263  * @extends Roo.data.DataProxy
12264  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12265  * configured to reference a certain URL.<br><br>
12266  * <p>
12267  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12268  * from which the running page was served.<br><br>
12269  * <p>
12270  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12271  * <p>
12272  * Be aware that to enable the browser to parse an XML document, the server must set
12273  * the Content-Type header in the HTTP response to "text/xml".
12274  * @constructor
12275  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12276  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12277  * will be used to make the request.
12278  */
12279 Roo.data.HttpProxy = function(conn){
12280     Roo.data.HttpProxy.superclass.constructor.call(this);
12281     // is conn a conn config or a real conn?
12282     this.conn = conn;
12283     this.useAjax = !conn || !conn.events;
12284   
12285 };
12286
12287 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12288     // thse are take from connection...
12289     
12290     /**
12291      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12292      */
12293     /**
12294      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12295      * extra parameters to each request made by this object. (defaults to undefined)
12296      */
12297     /**
12298      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12299      *  to each request made by this object. (defaults to undefined)
12300      */
12301     /**
12302      * @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)
12303      */
12304     /**
12305      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12306      */
12307      /**
12308      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12309      * @type Boolean
12310      */
12311   
12312
12313     /**
12314      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12315      * @type Boolean
12316      */
12317     /**
12318      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12319      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12320      * a finer-grained basis than the DataProxy events.
12321      */
12322     getConnection : function(){
12323         return this.useAjax ? Roo.Ajax : this.conn;
12324     },
12325
12326     /**
12327      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12328      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12329      * process that block using the passed callback.
12330      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12331      * for the request to the remote server.
12332      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12333      * object into a block of Roo.data.Records.
12334      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12335      * The function must be passed <ul>
12336      * <li>The Record block object</li>
12337      * <li>The "arg" argument from the load function</li>
12338      * <li>A boolean success indicator</li>
12339      * </ul>
12340      * @param {Object} scope The scope in which to call the callback
12341      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12342      */
12343     load : function(params, reader, callback, scope, arg){
12344         if(this.fireEvent("beforeload", this, params) !== false){
12345             var  o = {
12346                 params : params || {},
12347                 request: {
12348                     callback : callback,
12349                     scope : scope,
12350                     arg : arg
12351                 },
12352                 reader: reader,
12353                 callback : this.loadResponse,
12354                 scope: this
12355             };
12356             if(this.useAjax){
12357                 Roo.applyIf(o, this.conn);
12358                 if(this.activeRequest){
12359                     Roo.Ajax.abort(this.activeRequest);
12360                 }
12361                 this.activeRequest = Roo.Ajax.request(o);
12362             }else{
12363                 this.conn.request(o);
12364             }
12365         }else{
12366             callback.call(scope||this, null, arg, false);
12367         }
12368     },
12369
12370     // private
12371     loadResponse : function(o, success, response){
12372         delete this.activeRequest;
12373         if(!success){
12374             this.fireEvent("loadexception", this, o, response);
12375             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12376             return;
12377         }
12378         var result;
12379         try {
12380             result = o.reader.read(response);
12381         }catch(e){
12382             this.fireEvent("loadexception", this, o, response, e);
12383             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12384             return;
12385         }
12386         
12387         this.fireEvent("load", this, o, o.request.arg);
12388         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12389     },
12390
12391     // private
12392     update : function(dataSet){
12393
12394     },
12395
12396     // private
12397     updateResponse : function(dataSet){
12398
12399     }
12400 });/*
12401  * Based on:
12402  * Ext JS Library 1.1.1
12403  * Copyright(c) 2006-2007, Ext JS, LLC.
12404  *
12405  * Originally Released Under LGPL - original licence link has changed is not relivant.
12406  *
12407  * Fork - LGPL
12408  * <script type="text/javascript">
12409  */
12410
12411 /**
12412  * @class Roo.data.ScriptTagProxy
12413  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12414  * other than the originating domain of the running page.<br><br>
12415  * <p>
12416  * <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
12417  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12418  * <p>
12419  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12420  * source code that is used as the source inside a &lt;script> tag.<br><br>
12421  * <p>
12422  * In order for the browser to process the returned data, the server must wrap the data object
12423  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12424  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12425  * depending on whether the callback name was passed:
12426  * <p>
12427  * <pre><code>
12428 boolean scriptTag = false;
12429 String cb = request.getParameter("callback");
12430 if (cb != null) {
12431     scriptTag = true;
12432     response.setContentType("text/javascript");
12433 } else {
12434     response.setContentType("application/x-json");
12435 }
12436 Writer out = response.getWriter();
12437 if (scriptTag) {
12438     out.write(cb + "(");
12439 }
12440 out.print(dataBlock.toJsonString());
12441 if (scriptTag) {
12442     out.write(");");
12443 }
12444 </pre></code>
12445  *
12446  * @constructor
12447  * @param {Object} config A configuration object.
12448  */
12449 Roo.data.ScriptTagProxy = function(config){
12450     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12451     Roo.apply(this, config);
12452     this.head = document.getElementsByTagName("head")[0];
12453 };
12454
12455 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12456
12457 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12458     /**
12459      * @cfg {String} url The URL from which to request the data object.
12460      */
12461     /**
12462      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12463      */
12464     timeout : 30000,
12465     /**
12466      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12467      * the server the name of the callback function set up by the load call to process the returned data object.
12468      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12469      * javascript output which calls this named function passing the data object as its only parameter.
12470      */
12471     callbackParam : "callback",
12472     /**
12473      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12474      * name to the request.
12475      */
12476     nocache : true,
12477
12478     /**
12479      * Load data from the configured URL, read the data object into
12480      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12481      * process that block using the passed callback.
12482      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12483      * for the request to the remote server.
12484      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12485      * object into a block of Roo.data.Records.
12486      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12487      * The function must be passed <ul>
12488      * <li>The Record block object</li>
12489      * <li>The "arg" argument from the load function</li>
12490      * <li>A boolean success indicator</li>
12491      * </ul>
12492      * @param {Object} scope The scope in which to call the callback
12493      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12494      */
12495     load : function(params, reader, callback, scope, arg){
12496         if(this.fireEvent("beforeload", this, params) !== false){
12497
12498             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12499
12500             var url = this.url;
12501             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12502             if(this.nocache){
12503                 url += "&_dc=" + (new Date().getTime());
12504             }
12505             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12506             var trans = {
12507                 id : transId,
12508                 cb : "stcCallback"+transId,
12509                 scriptId : "stcScript"+transId,
12510                 params : params,
12511                 arg : arg,
12512                 url : url,
12513                 callback : callback,
12514                 scope : scope,
12515                 reader : reader
12516             };
12517             var conn = this;
12518
12519             window[trans.cb] = function(o){
12520                 conn.handleResponse(o, trans);
12521             };
12522
12523             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12524
12525             if(this.autoAbort !== false){
12526                 this.abort();
12527             }
12528
12529             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12530
12531             var script = document.createElement("script");
12532             script.setAttribute("src", url);
12533             script.setAttribute("type", "text/javascript");
12534             script.setAttribute("id", trans.scriptId);
12535             this.head.appendChild(script);
12536
12537             this.trans = trans;
12538         }else{
12539             callback.call(scope||this, null, arg, false);
12540         }
12541     },
12542
12543     // private
12544     isLoading : function(){
12545         return this.trans ? true : false;
12546     },
12547
12548     /**
12549      * Abort the current server request.
12550      */
12551     abort : function(){
12552         if(this.isLoading()){
12553             this.destroyTrans(this.trans);
12554         }
12555     },
12556
12557     // private
12558     destroyTrans : function(trans, isLoaded){
12559         this.head.removeChild(document.getElementById(trans.scriptId));
12560         clearTimeout(trans.timeoutId);
12561         if(isLoaded){
12562             window[trans.cb] = undefined;
12563             try{
12564                 delete window[trans.cb];
12565             }catch(e){}
12566         }else{
12567             // if hasn't been loaded, wait for load to remove it to prevent script error
12568             window[trans.cb] = function(){
12569                 window[trans.cb] = undefined;
12570                 try{
12571                     delete window[trans.cb];
12572                 }catch(e){}
12573             };
12574         }
12575     },
12576
12577     // private
12578     handleResponse : function(o, trans){
12579         this.trans = false;
12580         this.destroyTrans(trans, true);
12581         var result;
12582         try {
12583             result = trans.reader.readRecords(o);
12584         }catch(e){
12585             this.fireEvent("loadexception", this, o, trans.arg, e);
12586             trans.callback.call(trans.scope||window, null, trans.arg, false);
12587             return;
12588         }
12589         this.fireEvent("load", this, o, trans.arg);
12590         trans.callback.call(trans.scope||window, result, trans.arg, true);
12591     },
12592
12593     // private
12594     handleFailure : function(trans){
12595         this.trans = false;
12596         this.destroyTrans(trans, false);
12597         this.fireEvent("loadexception", this, null, trans.arg);
12598         trans.callback.call(trans.scope||window, null, trans.arg, false);
12599     }
12600 });/*
12601  * Based on:
12602  * Ext JS Library 1.1.1
12603  * Copyright(c) 2006-2007, Ext JS, LLC.
12604  *
12605  * Originally Released Under LGPL - original licence link has changed is not relivant.
12606  *
12607  * Fork - LGPL
12608  * <script type="text/javascript">
12609  */
12610
12611 /**
12612  * @class Roo.data.JsonReader
12613  * @extends Roo.data.DataReader
12614  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12615  * based on mappings in a provided Roo.data.Record constructor.
12616  * 
12617  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12618  * in the reply previously. 
12619  * 
12620  * <p>
12621  * Example code:
12622  * <pre><code>
12623 var RecordDef = Roo.data.Record.create([
12624     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12625     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12626 ]);
12627 var myReader = new Roo.data.JsonReader({
12628     totalProperty: "results",    // The property which contains the total dataset size (optional)
12629     root: "rows",                // The property which contains an Array of row objects
12630     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12631 }, RecordDef);
12632 </code></pre>
12633  * <p>
12634  * This would consume a JSON file like this:
12635  * <pre><code>
12636 { 'results': 2, 'rows': [
12637     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12638     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12639 }
12640 </code></pre>
12641  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12642  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12643  * paged from the remote server.
12644  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12645  * @cfg {String} root name of the property which contains the Array of row objects.
12646  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12647  * @cfg {Array} fields Array of field definition objects
12648  * @constructor
12649  * Create a new JsonReader
12650  * @param {Object} meta Metadata configuration options
12651  * @param {Object} recordType Either an Array of field definition objects,
12652  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12653  */
12654 Roo.data.JsonReader = function(meta, recordType){
12655     
12656     meta = meta || {};
12657     // set some defaults:
12658     Roo.applyIf(meta, {
12659         totalProperty: 'total',
12660         successProperty : 'success',
12661         root : 'data',
12662         id : 'id'
12663     });
12664     
12665     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12666 };
12667 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12668     
12669     /**
12670      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12671      * Used by Store query builder to append _requestMeta to params.
12672      * 
12673      */
12674     metaFromRemote : false,
12675     /**
12676      * This method is only used by a DataProxy which has retrieved data from a remote server.
12677      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12678      * @return {Object} data A data block which is used by an Roo.data.Store object as
12679      * a cache of Roo.data.Records.
12680      */
12681     read : function(response){
12682         var json = response.responseText;
12683        
12684         var o = /* eval:var:o */ eval("("+json+")");
12685         if(!o) {
12686             throw {message: "JsonReader.read: Json object not found"};
12687         }
12688         
12689         if(o.metaData){
12690             
12691             delete this.ef;
12692             this.metaFromRemote = true;
12693             this.meta = o.metaData;
12694             this.recordType = Roo.data.Record.create(o.metaData.fields);
12695             this.onMetaChange(this.meta, this.recordType, o);
12696         }
12697         return this.readRecords(o);
12698     },
12699
12700     // private function a store will implement
12701     onMetaChange : function(meta, recordType, o){
12702
12703     },
12704
12705     /**
12706          * @ignore
12707          */
12708     simpleAccess: function(obj, subsc) {
12709         return obj[subsc];
12710     },
12711
12712         /**
12713          * @ignore
12714          */
12715     getJsonAccessor: function(){
12716         var re = /[\[\.]/;
12717         return function(expr) {
12718             try {
12719                 return(re.test(expr))
12720                     ? new Function("obj", "return obj." + expr)
12721                     : function(obj){
12722                         return obj[expr];
12723                     };
12724             } catch(e){}
12725             return Roo.emptyFn;
12726         };
12727     }(),
12728
12729     /**
12730      * Create a data block containing Roo.data.Records from an XML document.
12731      * @param {Object} o An object which contains an Array of row objects in the property specified
12732      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12733      * which contains the total size of the dataset.
12734      * @return {Object} data A data block which is used by an Roo.data.Store object as
12735      * a cache of Roo.data.Records.
12736      */
12737     readRecords : function(o){
12738         /**
12739          * After any data loads, the raw JSON data is available for further custom processing.
12740          * @type Object
12741          */
12742         this.o = o;
12743         var s = this.meta, Record = this.recordType,
12744             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12745
12746 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12747         if (!this.ef) {
12748             if(s.totalProperty) {
12749                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12750                 }
12751                 if(s.successProperty) {
12752                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12753                 }
12754                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12755                 if (s.id) {
12756                         var g = this.getJsonAccessor(s.id);
12757                         this.getId = function(rec) {
12758                                 var r = g(rec);  
12759                                 return (r === undefined || r === "") ? null : r;
12760                         };
12761                 } else {
12762                         this.getId = function(){return null;};
12763                 }
12764             this.ef = [];
12765             for(var jj = 0; jj < fl; jj++){
12766                 f = fi[jj];
12767                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12768                 this.ef[jj] = this.getJsonAccessor(map);
12769             }
12770         }
12771
12772         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12773         if(s.totalProperty){
12774             var vt = parseInt(this.getTotal(o), 10);
12775             if(!isNaN(vt)){
12776                 totalRecords = vt;
12777             }
12778         }
12779         if(s.successProperty){
12780             var vs = this.getSuccess(o);
12781             if(vs === false || vs === 'false'){
12782                 success = false;
12783             }
12784         }
12785         var records = [];
12786         for(var i = 0; i < c; i++){
12787                 var n = root[i];
12788             var values = {};
12789             var id = this.getId(n);
12790             for(var j = 0; j < fl; j++){
12791                 f = fi[j];
12792             var v = this.ef[j](n);
12793             if (!f.convert) {
12794                 Roo.log('missing convert for ' + f.name);
12795                 Roo.log(f);
12796                 continue;
12797             }
12798             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12799             }
12800             var record = new Record(values, id);
12801             record.json = n;
12802             records[i] = record;
12803         }
12804         return {
12805             raw : o,
12806             success : success,
12807             records : records,
12808             totalRecords : totalRecords
12809         };
12810     }
12811 });/*
12812  * Based on:
12813  * Ext JS Library 1.1.1
12814  * Copyright(c) 2006-2007, Ext JS, LLC.
12815  *
12816  * Originally Released Under LGPL - original licence link has changed is not relivant.
12817  *
12818  * Fork - LGPL
12819  * <script type="text/javascript">
12820  */
12821
12822 /**
12823  * @class Roo.data.ArrayReader
12824  * @extends Roo.data.DataReader
12825  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12826  * Each element of that Array represents a row of data fields. The
12827  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12828  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12829  * <p>
12830  * Example code:.
12831  * <pre><code>
12832 var RecordDef = Roo.data.Record.create([
12833     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12834     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12835 ]);
12836 var myReader = new Roo.data.ArrayReader({
12837     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12838 }, RecordDef);
12839 </code></pre>
12840  * <p>
12841  * This would consume an Array like this:
12842  * <pre><code>
12843 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12844   </code></pre>
12845  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12846  * @constructor
12847  * Create a new JsonReader
12848  * @param {Object} meta Metadata configuration options.
12849  * @param {Object} recordType Either an Array of field definition objects
12850  * as specified to {@link Roo.data.Record#create},
12851  * or an {@link Roo.data.Record} object
12852  * created using {@link Roo.data.Record#create}.
12853  */
12854 Roo.data.ArrayReader = function(meta, recordType){
12855     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12856 };
12857
12858 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12859     /**
12860      * Create a data block containing Roo.data.Records from an XML document.
12861      * @param {Object} o An Array of row objects which represents the dataset.
12862      * @return {Object} data A data block which is used by an Roo.data.Store object as
12863      * a cache of Roo.data.Records.
12864      */
12865     readRecords : function(o){
12866         var sid = this.meta ? this.meta.id : null;
12867         var recordType = this.recordType, fields = recordType.prototype.fields;
12868         var records = [];
12869         var root = o;
12870             for(var i = 0; i < root.length; i++){
12871                     var n = root[i];
12872                 var values = {};
12873                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12874                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12875                 var f = fields.items[j];
12876                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12877                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12878                 v = f.convert(v);
12879                 values[f.name] = v;
12880             }
12881                 var record = new recordType(values, id);
12882                 record.json = n;
12883                 records[records.length] = record;
12884             }
12885             return {
12886                 records : records,
12887                 totalRecords : records.length
12888             };
12889     }
12890 });/*
12891  * - LGPL
12892  * * 
12893  */
12894
12895 /**
12896  * @class Roo.bootstrap.ComboBox
12897  * @extends Roo.bootstrap.TriggerField
12898  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12899  * @cfg {Boolean} append (true|false) default false
12900  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12901  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12902  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12903  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12904  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12905  * @cfg {Boolean} animate default true
12906  * @cfg {Boolean} emptyResultText only for touch device
12907  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12908  * @cfg {String} emptyTitle default ''
12909  * @constructor
12910  * Create a new ComboBox.
12911  * @param {Object} config Configuration options
12912  */
12913 Roo.bootstrap.ComboBox = function(config){
12914     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12915     this.addEvents({
12916         /**
12917          * @event expand
12918          * Fires when the dropdown list is expanded
12919         * @param {Roo.bootstrap.ComboBox} combo This combo box
12920         */
12921         'expand' : true,
12922         /**
12923          * @event collapse
12924          * Fires when the dropdown list is collapsed
12925         * @param {Roo.bootstrap.ComboBox} combo This combo box
12926         */
12927         'collapse' : true,
12928         /**
12929          * @event beforeselect
12930          * Fires before a list item is selected. Return false to cancel the selection.
12931         * @param {Roo.bootstrap.ComboBox} combo This combo box
12932         * @param {Roo.data.Record} record The data record returned from the underlying store
12933         * @param {Number} index The index of the selected item in the dropdown list
12934         */
12935         'beforeselect' : true,
12936         /**
12937          * @event select
12938          * Fires when a list item is selected
12939         * @param {Roo.bootstrap.ComboBox} combo This combo box
12940         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12941         * @param {Number} index The index of the selected item in the dropdown list
12942         */
12943         'select' : true,
12944         /**
12945          * @event beforequery
12946          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12947          * The event object passed has these properties:
12948         * @param {Roo.bootstrap.ComboBox} combo This combo box
12949         * @param {String} query The query
12950         * @param {Boolean} forceAll true to force "all" query
12951         * @param {Boolean} cancel true to cancel the query
12952         * @param {Object} e The query event object
12953         */
12954         'beforequery': true,
12955          /**
12956          * @event add
12957          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12958         * @param {Roo.bootstrap.ComboBox} combo This combo box
12959         */
12960         'add' : true,
12961         /**
12962          * @event edit
12963          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12964         * @param {Roo.bootstrap.ComboBox} combo This combo box
12965         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12966         */
12967         'edit' : true,
12968         /**
12969          * @event remove
12970          * Fires when the remove value from the combobox array
12971         * @param {Roo.bootstrap.ComboBox} combo This combo box
12972         */
12973         'remove' : true,
12974         /**
12975          * @event afterremove
12976          * Fires when the remove value from the combobox array
12977         * @param {Roo.bootstrap.ComboBox} combo This combo box
12978         */
12979         'afterremove' : true,
12980         /**
12981          * @event specialfilter
12982          * Fires when specialfilter
12983             * @param {Roo.bootstrap.ComboBox} combo This combo box
12984             */
12985         'specialfilter' : true,
12986         /**
12987          * @event tick
12988          * Fires when tick the element
12989             * @param {Roo.bootstrap.ComboBox} combo This combo box
12990             */
12991         'tick' : true,
12992         /**
12993          * @event touchviewdisplay
12994          * Fires when touch view require special display (default is using displayField)
12995             * @param {Roo.bootstrap.ComboBox} combo This combo box
12996             * @param {Object} cfg set html .
12997             */
12998         'touchviewdisplay' : true
12999         
13000     });
13001     
13002     this.item = [];
13003     this.tickItems = [];
13004     
13005     this.selectedIndex = -1;
13006     if(this.mode == 'local'){
13007         if(config.queryDelay === undefined){
13008             this.queryDelay = 10;
13009         }
13010         if(config.minChars === undefined){
13011             this.minChars = 0;
13012         }
13013     }
13014 };
13015
13016 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13017      
13018     /**
13019      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13020      * rendering into an Roo.Editor, defaults to false)
13021      */
13022     /**
13023      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13024      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13025      */
13026     /**
13027      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13028      */
13029     /**
13030      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13031      * the dropdown list (defaults to undefined, with no header element)
13032      */
13033
13034      /**
13035      * @cfg {String/Roo.Template} tpl The template to use to render the output
13036      */
13037      
13038      /**
13039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13040      */
13041     listWidth: undefined,
13042     /**
13043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13044      * mode = 'remote' or 'text' if mode = 'local')
13045      */
13046     displayField: undefined,
13047     
13048     /**
13049      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13050      * mode = 'remote' or 'value' if mode = 'local'). 
13051      * Note: use of a valueField requires the user make a selection
13052      * in order for a value to be mapped.
13053      */
13054     valueField: undefined,
13055     /**
13056      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13057      */
13058     modalTitle : '',
13059     
13060     /**
13061      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13062      * field's data value (defaults to the underlying DOM element's name)
13063      */
13064     hiddenName: undefined,
13065     /**
13066      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13067      */
13068     listClass: '',
13069     /**
13070      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13071      */
13072     selectedClass: 'active',
13073     
13074     /**
13075      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13076      */
13077     shadow:'sides',
13078     /**
13079      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13080      * anchor positions (defaults to 'tl-bl')
13081      */
13082     listAlign: 'tl-bl?',
13083     /**
13084      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13085      */
13086     maxHeight: 300,
13087     /**
13088      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13089      * query specified by the allQuery config option (defaults to 'query')
13090      */
13091     triggerAction: 'query',
13092     /**
13093      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13094      * (defaults to 4, does not apply if editable = false)
13095      */
13096     minChars : 4,
13097     /**
13098      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13099      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13100      */
13101     typeAhead: false,
13102     /**
13103      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13104      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13105      */
13106     queryDelay: 500,
13107     /**
13108      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13109      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13110      */
13111     pageSize: 0,
13112     /**
13113      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13114      * when editable = true (defaults to false)
13115      */
13116     selectOnFocus:false,
13117     /**
13118      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13119      */
13120     queryParam: 'query',
13121     /**
13122      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13123      * when mode = 'remote' (defaults to 'Loading...')
13124      */
13125     loadingText: 'Loading...',
13126     /**
13127      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13128      */
13129     resizable: false,
13130     /**
13131      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13132      */
13133     handleHeight : 8,
13134     /**
13135      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13136      * traditional select (defaults to true)
13137      */
13138     editable: true,
13139     /**
13140      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13141      */
13142     allQuery: '',
13143     /**
13144      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13145      */
13146     mode: 'remote',
13147     /**
13148      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13149      * listWidth has a higher value)
13150      */
13151     minListWidth : 70,
13152     /**
13153      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13154      * allow the user to set arbitrary text into the field (defaults to false)
13155      */
13156     forceSelection:false,
13157     /**
13158      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13159      * if typeAhead = true (defaults to 250)
13160      */
13161     typeAheadDelay : 250,
13162     /**
13163      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13164      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13165      */
13166     valueNotFoundText : undefined,
13167     /**
13168      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13169      */
13170     blockFocus : false,
13171     
13172     /**
13173      * @cfg {Boolean} disableClear Disable showing of clear button.
13174      */
13175     disableClear : false,
13176     /**
13177      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13178      */
13179     alwaysQuery : false,
13180     
13181     /**
13182      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13183      */
13184     multiple : false,
13185     
13186     /**
13187      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13188      */
13189     invalidClass : "has-warning",
13190     
13191     /**
13192      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13193      */
13194     validClass : "has-success",
13195     
13196     /**
13197      * @cfg {Boolean} specialFilter (true|false) special filter default false
13198      */
13199     specialFilter : false,
13200     
13201     /**
13202      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13203      */
13204     mobileTouchView : true,
13205     
13206     /**
13207      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13208      */
13209     useNativeIOS : false,
13210     
13211     /**
13212      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13213      */
13214     mobile_restrict_height : false,
13215     
13216     ios_options : false,
13217     
13218     //private
13219     addicon : false,
13220     editicon: false,
13221     
13222     page: 0,
13223     hasQuery: false,
13224     append: false,
13225     loadNext: false,
13226     autoFocus : true,
13227     tickable : false,
13228     btnPosition : 'right',
13229     triggerList : true,
13230     showToggleBtn : true,
13231     animate : true,
13232     emptyResultText: 'Empty',
13233     triggerText : 'Select',
13234     emptyTitle : '',
13235     
13236     // element that contains real text value.. (when hidden is used..)
13237     
13238     getAutoCreate : function()
13239     {   
13240         var cfg = false;
13241         //render
13242         /*
13243          * Render classic select for iso
13244          */
13245         
13246         if(Roo.isIOS && this.useNativeIOS){
13247             cfg = this.getAutoCreateNativeIOS();
13248             return cfg;
13249         }
13250         
13251         /*
13252          * Touch Devices
13253          */
13254         
13255         if(Roo.isTouch && this.mobileTouchView){
13256             cfg = this.getAutoCreateTouchView();
13257             return cfg;;
13258         }
13259         
13260         /*
13261          *  Normal ComboBox
13262          */
13263         if(!this.tickable){
13264             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13265             return cfg;
13266         }
13267         
13268         /*
13269          *  ComboBox with tickable selections
13270          */
13271              
13272         var align = this.labelAlign || this.parentLabelAlign();
13273         
13274         cfg = {
13275             cls : 'form-group roo-combobox-tickable' //input-group
13276         };
13277         
13278         var btn_text_select = '';
13279         var btn_text_done = '';
13280         var btn_text_cancel = '';
13281         
13282         if (this.btn_text_show) {
13283             btn_text_select = 'Select';
13284             btn_text_done = 'Done';
13285             btn_text_cancel = 'Cancel'; 
13286         }
13287         
13288         var buttons = {
13289             tag : 'div',
13290             cls : 'tickable-buttons',
13291             cn : [
13292                 {
13293                     tag : 'button',
13294                     type : 'button',
13295                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13296                     //html : this.triggerText
13297                     html: btn_text_select
13298                 },
13299                 {
13300                     tag : 'button',
13301                     type : 'button',
13302                     name : 'ok',
13303                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13304                     //html : 'Done'
13305                     html: btn_text_done
13306                 },
13307                 {
13308                     tag : 'button',
13309                     type : 'button',
13310                     name : 'cancel',
13311                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13312                     //html : 'Cancel'
13313                     html: btn_text_cancel
13314                 }
13315             ]
13316         };
13317         
13318         if(this.editable){
13319             buttons.cn.unshift({
13320                 tag: 'input',
13321                 cls: 'roo-select2-search-field-input'
13322             });
13323         }
13324         
13325         var _this = this;
13326         
13327         Roo.each(buttons.cn, function(c){
13328             if (_this.size) {
13329                 c.cls += ' btn-' + _this.size;
13330             }
13331
13332             if (_this.disabled) {
13333                 c.disabled = true;
13334             }
13335         });
13336         
13337         var box = {
13338             tag: 'div',
13339             cn: [
13340                 {
13341                     tag: 'input',
13342                     type : 'hidden',
13343                     cls: 'form-hidden-field'
13344                 },
13345                 {
13346                     tag: 'ul',
13347                     cls: 'roo-select2-choices',
13348                     cn:[
13349                         {
13350                             tag: 'li',
13351                             cls: 'roo-select2-search-field',
13352                             cn: [
13353                                 buttons
13354                             ]
13355                         }
13356                     ]
13357                 }
13358             ]
13359         };
13360         
13361         var combobox = {
13362             cls: 'roo-select2-container input-group roo-select2-container-multi',
13363             cn: [
13364                 
13365                 box
13366 //                {
13367 //                    tag: 'ul',
13368 //                    cls: 'typeahead typeahead-long dropdown-menu',
13369 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13370 //                }
13371             ]
13372         };
13373         
13374         if(this.hasFeedback && !this.allowBlank){
13375             
13376             var feedback = {
13377                 tag: 'span',
13378                 cls: 'glyphicon form-control-feedback'
13379             };
13380
13381             combobox.cn.push(feedback);
13382         }
13383         
13384         var indicator = {
13385             tag : 'i',
13386             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13387             tooltip : 'This field is required'
13388         };
13389         if (Roo.bootstrap.version == 4) {
13390             indicator = {
13391                 tag : 'i',
13392                 style : 'display:none'
13393             };
13394         }
13395         if (align ==='left' && this.fieldLabel.length) {
13396             
13397             cfg.cls += ' roo-form-group-label-left row';
13398             
13399             cfg.cn = [
13400                 indicator,
13401                 {
13402                     tag: 'label',
13403                     'for' :  id,
13404                     cls : 'control-label col-form-label',
13405                     html : this.fieldLabel
13406
13407                 },
13408                 {
13409                     cls : "", 
13410                     cn: [
13411                         combobox
13412                     ]
13413                 }
13414
13415             ];
13416             
13417             var labelCfg = cfg.cn[1];
13418             var contentCfg = cfg.cn[2];
13419             
13420
13421             if(this.indicatorpos == 'right'){
13422                 
13423                 cfg.cn = [
13424                     {
13425                         tag: 'label',
13426                         'for' :  id,
13427                         cls : 'control-label col-form-label',
13428                         cn : [
13429                             {
13430                                 tag : 'span',
13431                                 html : this.fieldLabel
13432                             },
13433                             indicator
13434                         ]
13435                     },
13436                     {
13437                         cls : "",
13438                         cn: [
13439                             combobox
13440                         ]
13441                     }
13442
13443                 ];
13444                 
13445                 
13446                 
13447                 labelCfg = cfg.cn[0];
13448                 contentCfg = cfg.cn[1];
13449             
13450             }
13451             
13452             if(this.labelWidth > 12){
13453                 labelCfg.style = "width: " + this.labelWidth + 'px';
13454             }
13455             
13456             if(this.labelWidth < 13 && this.labelmd == 0){
13457                 this.labelmd = this.labelWidth;
13458             }
13459             
13460             if(this.labellg > 0){
13461                 labelCfg.cls += ' col-lg-' + this.labellg;
13462                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13463             }
13464             
13465             if(this.labelmd > 0){
13466                 labelCfg.cls += ' col-md-' + this.labelmd;
13467                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13468             }
13469             
13470             if(this.labelsm > 0){
13471                 labelCfg.cls += ' col-sm-' + this.labelsm;
13472                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13473             }
13474             
13475             if(this.labelxs > 0){
13476                 labelCfg.cls += ' col-xs-' + this.labelxs;
13477                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13478             }
13479                 
13480                 
13481         } else if ( this.fieldLabel.length) {
13482 //                Roo.log(" label");
13483                  cfg.cn = [
13484                    indicator,
13485                     {
13486                         tag: 'label',
13487                         //cls : 'input-group-addon',
13488                         html : this.fieldLabel
13489                     },
13490                     combobox
13491                 ];
13492                 
13493                 if(this.indicatorpos == 'right'){
13494                     cfg.cn = [
13495                         {
13496                             tag: 'label',
13497                             //cls : 'input-group-addon',
13498                             html : this.fieldLabel
13499                         },
13500                         indicator,
13501                         combobox
13502                     ];
13503                     
13504                 }
13505
13506         } else {
13507             
13508 //                Roo.log(" no label && no align");
13509                 cfg = combobox
13510                      
13511                 
13512         }
13513          
13514         var settings=this;
13515         ['xs','sm','md','lg'].map(function(size){
13516             if (settings[size]) {
13517                 cfg.cls += ' col-' + size + '-' + settings[size];
13518             }
13519         });
13520         
13521         return cfg;
13522         
13523     },
13524     
13525     _initEventsCalled : false,
13526     
13527     // private
13528     initEvents: function()
13529     {   
13530         if (this._initEventsCalled) { // as we call render... prevent looping...
13531             return;
13532         }
13533         this._initEventsCalled = true;
13534         
13535         if (!this.store) {
13536             throw "can not find store for combo";
13537         }
13538         
13539         this.indicator = this.indicatorEl();
13540         
13541         this.store = Roo.factory(this.store, Roo.data);
13542         this.store.parent = this;
13543         
13544         // if we are building from html. then this element is so complex, that we can not really
13545         // use the rendered HTML.
13546         // so we have to trash and replace the previous code.
13547         if (Roo.XComponent.build_from_html) {
13548             // remove this element....
13549             var e = this.el.dom, k=0;
13550             while (e ) { e = e.previousSibling;  ++k;}
13551
13552             this.el.remove();
13553             
13554             this.el=false;
13555             this.rendered = false;
13556             
13557             this.render(this.parent().getChildContainer(true), k);
13558         }
13559         
13560         if(Roo.isIOS && this.useNativeIOS){
13561             this.initIOSView();
13562             return;
13563         }
13564         
13565         /*
13566          * Touch Devices
13567          */
13568         
13569         if(Roo.isTouch && this.mobileTouchView){
13570             this.initTouchView();
13571             return;
13572         }
13573         
13574         if(this.tickable){
13575             this.initTickableEvents();
13576             return;
13577         }
13578         
13579         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13580         
13581         if(this.hiddenName){
13582             
13583             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13584             
13585             this.hiddenField.dom.value =
13586                 this.hiddenValue !== undefined ? this.hiddenValue :
13587                 this.value !== undefined ? this.value : '';
13588
13589             // prevent input submission
13590             this.el.dom.removeAttribute('name');
13591             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13592              
13593              
13594         }
13595         //if(Roo.isGecko){
13596         //    this.el.dom.setAttribute('autocomplete', 'off');
13597         //}
13598         
13599         var cls = 'x-combo-list';
13600         
13601         //this.list = new Roo.Layer({
13602         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13603         //});
13604         
13605         var _this = this;
13606         
13607         (function(){
13608             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13609             _this.list.setWidth(lw);
13610         }).defer(100);
13611         
13612         this.list.on('mouseover', this.onViewOver, this);
13613         this.list.on('mousemove', this.onViewMove, this);
13614         this.list.on('scroll', this.onViewScroll, this);
13615         
13616         /*
13617         this.list.swallowEvent('mousewheel');
13618         this.assetHeight = 0;
13619
13620         if(this.title){
13621             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13622             this.assetHeight += this.header.getHeight();
13623         }
13624
13625         this.innerList = this.list.createChild({cls:cls+'-inner'});
13626         this.innerList.on('mouseover', this.onViewOver, this);
13627         this.innerList.on('mousemove', this.onViewMove, this);
13628         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13629         
13630         if(this.allowBlank && !this.pageSize && !this.disableClear){
13631             this.footer = this.list.createChild({cls:cls+'-ft'});
13632             this.pageTb = new Roo.Toolbar(this.footer);
13633            
13634         }
13635         if(this.pageSize){
13636             this.footer = this.list.createChild({cls:cls+'-ft'});
13637             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13638                     {pageSize: this.pageSize});
13639             
13640         }
13641         
13642         if (this.pageTb && this.allowBlank && !this.disableClear) {
13643             var _this = this;
13644             this.pageTb.add(new Roo.Toolbar.Fill(), {
13645                 cls: 'x-btn-icon x-btn-clear',
13646                 text: '&#160;',
13647                 handler: function()
13648                 {
13649                     _this.collapse();
13650                     _this.clearValue();
13651                     _this.onSelect(false, -1);
13652                 }
13653             });
13654         }
13655         if (this.footer) {
13656             this.assetHeight += this.footer.getHeight();
13657         }
13658         */
13659             
13660         if(!this.tpl){
13661             this.tpl = Roo.bootstrap.version == 4 ?
13662                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13663                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13664         }
13665
13666         this.view = new Roo.View(this.list, this.tpl, {
13667             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13668         });
13669         //this.view.wrapEl.setDisplayed(false);
13670         this.view.on('click', this.onViewClick, this);
13671         
13672         
13673         this.store.on('beforeload', this.onBeforeLoad, this);
13674         this.store.on('load', this.onLoad, this);
13675         this.store.on('loadexception', this.onLoadException, this);
13676         /*
13677         if(this.resizable){
13678             this.resizer = new Roo.Resizable(this.list,  {
13679                pinned:true, handles:'se'
13680             });
13681             this.resizer.on('resize', function(r, w, h){
13682                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13683                 this.listWidth = w;
13684                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13685                 this.restrictHeight();
13686             }, this);
13687             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13688         }
13689         */
13690         if(!this.editable){
13691             this.editable = true;
13692             this.setEditable(false);
13693         }
13694         
13695         /*
13696         
13697         if (typeof(this.events.add.listeners) != 'undefined') {
13698             
13699             this.addicon = this.wrap.createChild(
13700                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13701        
13702             this.addicon.on('click', function(e) {
13703                 this.fireEvent('add', this);
13704             }, this);
13705         }
13706         if (typeof(this.events.edit.listeners) != 'undefined') {
13707             
13708             this.editicon = this.wrap.createChild(
13709                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13710             if (this.addicon) {
13711                 this.editicon.setStyle('margin-left', '40px');
13712             }
13713             this.editicon.on('click', function(e) {
13714                 
13715                 // we fire even  if inothing is selected..
13716                 this.fireEvent('edit', this, this.lastData );
13717                 
13718             }, this);
13719         }
13720         */
13721         
13722         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13723             "up" : function(e){
13724                 this.inKeyMode = true;
13725                 this.selectPrev();
13726             },
13727
13728             "down" : function(e){
13729                 if(!this.isExpanded()){
13730                     this.onTriggerClick();
13731                 }else{
13732                     this.inKeyMode = true;
13733                     this.selectNext();
13734                 }
13735             },
13736
13737             "enter" : function(e){
13738 //                this.onViewClick();
13739                 //return true;
13740                 this.collapse();
13741                 
13742                 if(this.fireEvent("specialkey", this, e)){
13743                     this.onViewClick(false);
13744                 }
13745                 
13746                 return true;
13747             },
13748
13749             "esc" : function(e){
13750                 this.collapse();
13751             },
13752
13753             "tab" : function(e){
13754                 this.collapse();
13755                 
13756                 if(this.fireEvent("specialkey", this, e)){
13757                     this.onViewClick(false);
13758                 }
13759                 
13760                 return true;
13761             },
13762
13763             scope : this,
13764
13765             doRelay : function(foo, bar, hname){
13766                 if(hname == 'down' || this.scope.isExpanded()){
13767                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13768                 }
13769                 return true;
13770             },
13771
13772             forceKeyDown: true
13773         });
13774         
13775         
13776         this.queryDelay = Math.max(this.queryDelay || 10,
13777                 this.mode == 'local' ? 10 : 250);
13778         
13779         
13780         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13781         
13782         if(this.typeAhead){
13783             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13784         }
13785         if(this.editable !== false){
13786             this.inputEl().on("keyup", this.onKeyUp, this);
13787         }
13788         if(this.forceSelection){
13789             this.inputEl().on('blur', this.doForce, this);
13790         }
13791         
13792         if(this.multiple){
13793             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13794             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13795         }
13796     },
13797     
13798     initTickableEvents: function()
13799     {   
13800         this.createList();
13801         
13802         if(this.hiddenName){
13803             
13804             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13805             
13806             this.hiddenField.dom.value =
13807                 this.hiddenValue !== undefined ? this.hiddenValue :
13808                 this.value !== undefined ? this.value : '';
13809
13810             // prevent input submission
13811             this.el.dom.removeAttribute('name');
13812             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13813              
13814              
13815         }
13816         
13817 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13818         
13819         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13820         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13821         if(this.triggerList){
13822             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13823         }
13824          
13825         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13826         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13827         
13828         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13829         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13830         
13831         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13832         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13833         
13834         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13835         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13836         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13837         
13838         this.okBtn.hide();
13839         this.cancelBtn.hide();
13840         
13841         var _this = this;
13842         
13843         (function(){
13844             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13845             _this.list.setWidth(lw);
13846         }).defer(100);
13847         
13848         this.list.on('mouseover', this.onViewOver, this);
13849         this.list.on('mousemove', this.onViewMove, this);
13850         
13851         this.list.on('scroll', this.onViewScroll, this);
13852         
13853         if(!this.tpl){
13854             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13855                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13856         }
13857
13858         this.view = new Roo.View(this.list, this.tpl, {
13859             singleSelect:true,
13860             tickable:true,
13861             parent:this,
13862             store: this.store,
13863             selectedClass: this.selectedClass
13864         });
13865         
13866         //this.view.wrapEl.setDisplayed(false);
13867         this.view.on('click', this.onViewClick, this);
13868         
13869         
13870         
13871         this.store.on('beforeload', this.onBeforeLoad, this);
13872         this.store.on('load', this.onLoad, this);
13873         this.store.on('loadexception', this.onLoadException, this);
13874         
13875         if(this.editable){
13876             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13877                 "up" : function(e){
13878                     this.inKeyMode = true;
13879                     this.selectPrev();
13880                 },
13881
13882                 "down" : function(e){
13883                     this.inKeyMode = true;
13884                     this.selectNext();
13885                 },
13886
13887                 "enter" : function(e){
13888                     if(this.fireEvent("specialkey", this, e)){
13889                         this.onViewClick(false);
13890                     }
13891                     
13892                     return true;
13893                 },
13894
13895                 "esc" : function(e){
13896                     this.onTickableFooterButtonClick(e, false, false);
13897                 },
13898
13899                 "tab" : function(e){
13900                     this.fireEvent("specialkey", this, e);
13901                     
13902                     this.onTickableFooterButtonClick(e, false, false);
13903                     
13904                     return true;
13905                 },
13906
13907                 scope : this,
13908
13909                 doRelay : function(e, fn, key){
13910                     if(this.scope.isExpanded()){
13911                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13912                     }
13913                     return true;
13914                 },
13915
13916                 forceKeyDown: true
13917             });
13918         }
13919         
13920         this.queryDelay = Math.max(this.queryDelay || 10,
13921                 this.mode == 'local' ? 10 : 250);
13922         
13923         
13924         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13925         
13926         if(this.typeAhead){
13927             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13928         }
13929         
13930         if(this.editable !== false){
13931             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13932         }
13933         
13934         this.indicator = this.indicatorEl();
13935         
13936         if(this.indicator){
13937             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13938             this.indicator.hide();
13939         }
13940         
13941     },
13942
13943     onDestroy : function(){
13944         if(this.view){
13945             this.view.setStore(null);
13946             this.view.el.removeAllListeners();
13947             this.view.el.remove();
13948             this.view.purgeListeners();
13949         }
13950         if(this.list){
13951             this.list.dom.innerHTML  = '';
13952         }
13953         
13954         if(this.store){
13955             this.store.un('beforeload', this.onBeforeLoad, this);
13956             this.store.un('load', this.onLoad, this);
13957             this.store.un('loadexception', this.onLoadException, this);
13958         }
13959         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13960     },
13961
13962     // private
13963     fireKey : function(e){
13964         if(e.isNavKeyPress() && !this.list.isVisible()){
13965             this.fireEvent("specialkey", this, e);
13966         }
13967     },
13968
13969     // private
13970     onResize: function(w, h){
13971 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13972 //        
13973 //        if(typeof w != 'number'){
13974 //            // we do not handle it!?!?
13975 //            return;
13976 //        }
13977 //        var tw = this.trigger.getWidth();
13978 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13979 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13980 //        var x = w - tw;
13981 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13982 //            
13983 //        //this.trigger.setStyle('left', x+'px');
13984 //        
13985 //        if(this.list && this.listWidth === undefined){
13986 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13987 //            this.list.setWidth(lw);
13988 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13989 //        }
13990         
13991     
13992         
13993     },
13994
13995     /**
13996      * Allow or prevent the user from directly editing the field text.  If false is passed,
13997      * the user will only be able to select from the items defined in the dropdown list.  This method
13998      * is the runtime equivalent of setting the 'editable' config option at config time.
13999      * @param {Boolean} value True to allow the user to directly edit the field text
14000      */
14001     setEditable : function(value){
14002         if(value == this.editable){
14003             return;
14004         }
14005         this.editable = value;
14006         if(!value){
14007             this.inputEl().dom.setAttribute('readOnly', true);
14008             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14009             this.inputEl().addClass('x-combo-noedit');
14010         }else{
14011             this.inputEl().dom.setAttribute('readOnly', false);
14012             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14013             this.inputEl().removeClass('x-combo-noedit');
14014         }
14015     },
14016
14017     // private
14018     
14019     onBeforeLoad : function(combo,opts){
14020         if(!this.hasFocus){
14021             return;
14022         }
14023          if (!opts.add) {
14024             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14025          }
14026         this.restrictHeight();
14027         this.selectedIndex = -1;
14028     },
14029
14030     // private
14031     onLoad : function(){
14032         
14033         this.hasQuery = false;
14034         
14035         if(!this.hasFocus){
14036             return;
14037         }
14038         
14039         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14040             this.loading.hide();
14041         }
14042         
14043         if(this.store.getCount() > 0){
14044             
14045             this.expand();
14046             this.restrictHeight();
14047             if(this.lastQuery == this.allQuery){
14048                 if(this.editable && !this.tickable){
14049                     this.inputEl().dom.select();
14050                 }
14051                 
14052                 if(
14053                     !this.selectByValue(this.value, true) &&
14054                     this.autoFocus && 
14055                     (
14056                         !this.store.lastOptions ||
14057                         typeof(this.store.lastOptions.add) == 'undefined' || 
14058                         this.store.lastOptions.add != true
14059                     )
14060                 ){
14061                     this.select(0, true);
14062                 }
14063             }else{
14064                 if(this.autoFocus){
14065                     this.selectNext();
14066                 }
14067                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14068                     this.taTask.delay(this.typeAheadDelay);
14069                 }
14070             }
14071         }else{
14072             this.onEmptyResults();
14073         }
14074         
14075         //this.el.focus();
14076     },
14077     // private
14078     onLoadException : function()
14079     {
14080         this.hasQuery = false;
14081         
14082         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14083             this.loading.hide();
14084         }
14085         
14086         if(this.tickable && this.editable){
14087             return;
14088         }
14089         
14090         this.collapse();
14091         // only causes errors at present
14092         //Roo.log(this.store.reader.jsonData);
14093         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14094             // fixme
14095             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14096         //}
14097         
14098         
14099     },
14100     // private
14101     onTypeAhead : function(){
14102         if(this.store.getCount() > 0){
14103             var r = this.store.getAt(0);
14104             var newValue = r.data[this.displayField];
14105             var len = newValue.length;
14106             var selStart = this.getRawValue().length;
14107             
14108             if(selStart != len){
14109                 this.setRawValue(newValue);
14110                 this.selectText(selStart, newValue.length);
14111             }
14112         }
14113     },
14114
14115     // private
14116     onSelect : function(record, index){
14117         
14118         if(this.fireEvent('beforeselect', this, record, index) !== false){
14119         
14120             this.setFromData(index > -1 ? record.data : false);
14121             
14122             this.collapse();
14123             this.fireEvent('select', this, record, index);
14124         }
14125     },
14126
14127     /**
14128      * Returns the currently selected field value or empty string if no value is set.
14129      * @return {String} value The selected value
14130      */
14131     getValue : function()
14132     {
14133         if(Roo.isIOS && this.useNativeIOS){
14134             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14135         }
14136         
14137         if(this.multiple){
14138             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14139         }
14140         
14141         if(this.valueField){
14142             return typeof this.value != 'undefined' ? this.value : '';
14143         }else{
14144             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14145         }
14146     },
14147     
14148     getRawValue : function()
14149     {
14150         if(Roo.isIOS && this.useNativeIOS){
14151             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14152         }
14153         
14154         var v = this.inputEl().getValue();
14155         
14156         return v;
14157     },
14158
14159     /**
14160      * Clears any text/value currently set in the field
14161      */
14162     clearValue : function(){
14163         
14164         if(this.hiddenField){
14165             this.hiddenField.dom.value = '';
14166         }
14167         this.value = '';
14168         this.setRawValue('');
14169         this.lastSelectionText = '';
14170         this.lastData = false;
14171         
14172         var close = this.closeTriggerEl();
14173         
14174         if(close){
14175             close.hide();
14176         }
14177         
14178         this.validate();
14179         
14180     },
14181
14182     /**
14183      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14184      * will be displayed in the field.  If the value does not match the data value of an existing item,
14185      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14186      * Otherwise the field will be blank (although the value will still be set).
14187      * @param {String} value The value to match
14188      */
14189     setValue : function(v)
14190     {
14191         if(Roo.isIOS && this.useNativeIOS){
14192             this.setIOSValue(v);
14193             return;
14194         }
14195         
14196         if(this.multiple){
14197             this.syncValue();
14198             return;
14199         }
14200         
14201         var text = v;
14202         if(this.valueField){
14203             var r = this.findRecord(this.valueField, v);
14204             if(r){
14205                 text = r.data[this.displayField];
14206             }else if(this.valueNotFoundText !== undefined){
14207                 text = this.valueNotFoundText;
14208             }
14209         }
14210         this.lastSelectionText = text;
14211         if(this.hiddenField){
14212             this.hiddenField.dom.value = v;
14213         }
14214         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14215         this.value = v;
14216         
14217         var close = this.closeTriggerEl();
14218         
14219         if(close){
14220             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14221         }
14222         
14223         this.validate();
14224     },
14225     /**
14226      * @property {Object} the last set data for the element
14227      */
14228     
14229     lastData : false,
14230     /**
14231      * Sets the value of the field based on a object which is related to the record format for the store.
14232      * @param {Object} value the value to set as. or false on reset?
14233      */
14234     setFromData : function(o){
14235         
14236         if(this.multiple){
14237             this.addItem(o);
14238             return;
14239         }
14240             
14241         var dv = ''; // display value
14242         var vv = ''; // value value..
14243         this.lastData = o;
14244         if (this.displayField) {
14245             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14246         } else {
14247             // this is an error condition!!!
14248             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14249         }
14250         
14251         if(this.valueField){
14252             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14253         }
14254         
14255         var close = this.closeTriggerEl();
14256         
14257         if(close){
14258             if(dv.length || vv * 1 > 0){
14259                 close.show() ;
14260                 this.blockFocus=true;
14261             } else {
14262                 close.hide();
14263             }             
14264         }
14265         
14266         if(this.hiddenField){
14267             this.hiddenField.dom.value = vv;
14268             
14269             this.lastSelectionText = dv;
14270             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14271             this.value = vv;
14272             return;
14273         }
14274         // no hidden field.. - we store the value in 'value', but still display
14275         // display field!!!!
14276         this.lastSelectionText = dv;
14277         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14278         this.value = vv;
14279         
14280         
14281         
14282     },
14283     // private
14284     reset : function(){
14285         // overridden so that last data is reset..
14286         
14287         if(this.multiple){
14288             this.clearItem();
14289             return;
14290         }
14291         
14292         this.setValue(this.originalValue);
14293         //this.clearInvalid();
14294         this.lastData = false;
14295         if (this.view) {
14296             this.view.clearSelections();
14297         }
14298         
14299         this.validate();
14300     },
14301     // private
14302     findRecord : function(prop, value){
14303         var record;
14304         if(this.store.getCount() > 0){
14305             this.store.each(function(r){
14306                 if(r.data[prop] == value){
14307                     record = r;
14308                     return false;
14309                 }
14310                 return true;
14311             });
14312         }
14313         return record;
14314     },
14315     
14316     getName: function()
14317     {
14318         // returns hidden if it's set..
14319         if (!this.rendered) {return ''};
14320         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14321         
14322     },
14323     // private
14324     onViewMove : function(e, t){
14325         this.inKeyMode = false;
14326     },
14327
14328     // private
14329     onViewOver : function(e, t){
14330         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14331             return;
14332         }
14333         var item = this.view.findItemFromChild(t);
14334         
14335         if(item){
14336             var index = this.view.indexOf(item);
14337             this.select(index, false);
14338         }
14339     },
14340
14341     // private
14342     onViewClick : function(view, doFocus, el, e)
14343     {
14344         var index = this.view.getSelectedIndexes()[0];
14345         
14346         var r = this.store.getAt(index);
14347         
14348         if(this.tickable){
14349             
14350             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14351                 return;
14352             }
14353             
14354             var rm = false;
14355             var _this = this;
14356             
14357             Roo.each(this.tickItems, function(v,k){
14358                 
14359                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14360                     Roo.log(v);
14361                     _this.tickItems.splice(k, 1);
14362                     
14363                     if(typeof(e) == 'undefined' && view == false){
14364                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14365                     }
14366                     
14367                     rm = true;
14368                     return;
14369                 }
14370             });
14371             
14372             if(rm){
14373                 return;
14374             }
14375             
14376             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14377                 this.tickItems.push(r.data);
14378             }
14379             
14380             if(typeof(e) == 'undefined' && view == false){
14381                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14382             }
14383                     
14384             return;
14385         }
14386         
14387         if(r){
14388             this.onSelect(r, index);
14389         }
14390         if(doFocus !== false && !this.blockFocus){
14391             this.inputEl().focus();
14392         }
14393     },
14394
14395     // private
14396     restrictHeight : function(){
14397         //this.innerList.dom.style.height = '';
14398         //var inner = this.innerList.dom;
14399         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14400         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14401         //this.list.beginUpdate();
14402         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14403         this.list.alignTo(this.inputEl(), this.listAlign);
14404         this.list.alignTo(this.inputEl(), this.listAlign);
14405         //this.list.endUpdate();
14406     },
14407
14408     // private
14409     onEmptyResults : function(){
14410         
14411         if(this.tickable && this.editable){
14412             this.hasFocus = false;
14413             this.restrictHeight();
14414             return;
14415         }
14416         
14417         this.collapse();
14418     },
14419
14420     /**
14421      * Returns true if the dropdown list is expanded, else false.
14422      */
14423     isExpanded : function(){
14424         return this.list.isVisible();
14425     },
14426
14427     /**
14428      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14429      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14430      * @param {String} value The data value of the item to select
14431      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14432      * selected item if it is not currently in view (defaults to true)
14433      * @return {Boolean} True if the value matched an item in the list, else false
14434      */
14435     selectByValue : function(v, scrollIntoView){
14436         if(v !== undefined && v !== null){
14437             var r = this.findRecord(this.valueField || this.displayField, v);
14438             if(r){
14439                 this.select(this.store.indexOf(r), scrollIntoView);
14440                 return true;
14441             }
14442         }
14443         return false;
14444     },
14445
14446     /**
14447      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14448      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14449      * @param {Number} index The zero-based index of the list item to select
14450      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14451      * selected item if it is not currently in view (defaults to true)
14452      */
14453     select : function(index, scrollIntoView){
14454         this.selectedIndex = index;
14455         this.view.select(index);
14456         if(scrollIntoView !== false){
14457             var el = this.view.getNode(index);
14458             /*
14459              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14460              */
14461             if(el){
14462                 this.list.scrollChildIntoView(el, false);
14463             }
14464         }
14465     },
14466
14467     // private
14468     selectNext : function(){
14469         var ct = this.store.getCount();
14470         if(ct > 0){
14471             if(this.selectedIndex == -1){
14472                 this.select(0);
14473             }else if(this.selectedIndex < ct-1){
14474                 this.select(this.selectedIndex+1);
14475             }
14476         }
14477     },
14478
14479     // private
14480     selectPrev : function(){
14481         var ct = this.store.getCount();
14482         if(ct > 0){
14483             if(this.selectedIndex == -1){
14484                 this.select(0);
14485             }else if(this.selectedIndex != 0){
14486                 this.select(this.selectedIndex-1);
14487             }
14488         }
14489     },
14490
14491     // private
14492     onKeyUp : function(e){
14493         if(this.editable !== false && !e.isSpecialKey()){
14494             this.lastKey = e.getKey();
14495             this.dqTask.delay(this.queryDelay);
14496         }
14497     },
14498
14499     // private
14500     validateBlur : function(){
14501         return !this.list || !this.list.isVisible();   
14502     },
14503
14504     // private
14505     initQuery : function(){
14506         
14507         var v = this.getRawValue();
14508         
14509         if(this.tickable && this.editable){
14510             v = this.tickableInputEl().getValue();
14511         }
14512         
14513         this.doQuery(v);
14514     },
14515
14516     // private
14517     doForce : function(){
14518         if(this.inputEl().dom.value.length > 0){
14519             this.inputEl().dom.value =
14520                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14521              
14522         }
14523     },
14524
14525     /**
14526      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14527      * query allowing the query action to be canceled if needed.
14528      * @param {String} query The SQL query to execute
14529      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14530      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14531      * saved in the current store (defaults to false)
14532      */
14533     doQuery : function(q, forceAll){
14534         
14535         if(q === undefined || q === null){
14536             q = '';
14537         }
14538         var qe = {
14539             query: q,
14540             forceAll: forceAll,
14541             combo: this,
14542             cancel:false
14543         };
14544         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14545             return false;
14546         }
14547         q = qe.query;
14548         
14549         forceAll = qe.forceAll;
14550         if(forceAll === true || (q.length >= this.minChars)){
14551             
14552             this.hasQuery = true;
14553             
14554             if(this.lastQuery != q || this.alwaysQuery){
14555                 this.lastQuery = q;
14556                 if(this.mode == 'local'){
14557                     this.selectedIndex = -1;
14558                     if(forceAll){
14559                         this.store.clearFilter();
14560                     }else{
14561                         
14562                         if(this.specialFilter){
14563                             this.fireEvent('specialfilter', this);
14564                             this.onLoad();
14565                             return;
14566                         }
14567                         
14568                         this.store.filter(this.displayField, q);
14569                     }
14570                     
14571                     this.store.fireEvent("datachanged", this.store);
14572                     
14573                     this.onLoad();
14574                     
14575                     
14576                 }else{
14577                     
14578                     this.store.baseParams[this.queryParam] = q;
14579                     
14580                     var options = {params : this.getParams(q)};
14581                     
14582                     if(this.loadNext){
14583                         options.add = true;
14584                         options.params.start = this.page * this.pageSize;
14585                     }
14586                     
14587                     this.store.load(options);
14588                     
14589                     /*
14590                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14591                      *  we should expand the list on onLoad
14592                      *  so command out it
14593                      */
14594 //                    this.expand();
14595                 }
14596             }else{
14597                 this.selectedIndex = -1;
14598                 this.onLoad();   
14599             }
14600         }
14601         
14602         this.loadNext = false;
14603     },
14604     
14605     // private
14606     getParams : function(q){
14607         var p = {};
14608         //p[this.queryParam] = q;
14609         
14610         if(this.pageSize){
14611             p.start = 0;
14612             p.limit = this.pageSize;
14613         }
14614         return p;
14615     },
14616
14617     /**
14618      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14619      */
14620     collapse : function(){
14621         if(!this.isExpanded()){
14622             return;
14623         }
14624         
14625         this.list.hide();
14626         
14627         this.hasFocus = false;
14628         
14629         if(this.tickable){
14630             this.okBtn.hide();
14631             this.cancelBtn.hide();
14632             this.trigger.show();
14633             
14634             if(this.editable){
14635                 this.tickableInputEl().dom.value = '';
14636                 this.tickableInputEl().blur();
14637             }
14638             
14639         }
14640         
14641         Roo.get(document).un('mousedown', this.collapseIf, this);
14642         Roo.get(document).un('mousewheel', this.collapseIf, this);
14643         if (!this.editable) {
14644             Roo.get(document).un('keydown', this.listKeyPress, this);
14645         }
14646         this.fireEvent('collapse', this);
14647         
14648         this.validate();
14649     },
14650
14651     // private
14652     collapseIf : function(e){
14653         var in_combo  = e.within(this.el);
14654         var in_list =  e.within(this.list);
14655         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14656         
14657         if (in_combo || in_list || is_list) {
14658             //e.stopPropagation();
14659             return;
14660         }
14661         
14662         if(this.tickable){
14663             this.onTickableFooterButtonClick(e, false, false);
14664         }
14665
14666         this.collapse();
14667         
14668     },
14669
14670     /**
14671      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14672      */
14673     expand : function(){
14674        
14675         if(this.isExpanded() || !this.hasFocus){
14676             return;
14677         }
14678         
14679         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14680         this.list.setWidth(lw);
14681         
14682         Roo.log('expand');
14683         
14684         this.list.show();
14685         
14686         this.restrictHeight();
14687         
14688         if(this.tickable){
14689             
14690             this.tickItems = Roo.apply([], this.item);
14691             
14692             this.okBtn.show();
14693             this.cancelBtn.show();
14694             this.trigger.hide();
14695             
14696             if(this.editable){
14697                 this.tickableInputEl().focus();
14698             }
14699             
14700         }
14701         
14702         Roo.get(document).on('mousedown', this.collapseIf, this);
14703         Roo.get(document).on('mousewheel', this.collapseIf, this);
14704         if (!this.editable) {
14705             Roo.get(document).on('keydown', this.listKeyPress, this);
14706         }
14707         
14708         this.fireEvent('expand', this);
14709     },
14710
14711     // private
14712     // Implements the default empty TriggerField.onTriggerClick function
14713     onTriggerClick : function(e)
14714     {
14715         Roo.log('trigger click');
14716         
14717         if(this.disabled || !this.triggerList){
14718             return;
14719         }
14720         
14721         this.page = 0;
14722         this.loadNext = false;
14723         
14724         if(this.isExpanded()){
14725             this.collapse();
14726             if (!this.blockFocus) {
14727                 this.inputEl().focus();
14728             }
14729             
14730         }else {
14731             this.hasFocus = true;
14732             if(this.triggerAction == 'all') {
14733                 this.doQuery(this.allQuery, true);
14734             } else {
14735                 this.doQuery(this.getRawValue());
14736             }
14737             if (!this.blockFocus) {
14738                 this.inputEl().focus();
14739             }
14740         }
14741     },
14742     
14743     onTickableTriggerClick : function(e)
14744     {
14745         if(this.disabled){
14746             return;
14747         }
14748         
14749         this.page = 0;
14750         this.loadNext = false;
14751         this.hasFocus = true;
14752         
14753         if(this.triggerAction == 'all') {
14754             this.doQuery(this.allQuery, true);
14755         } else {
14756             this.doQuery(this.getRawValue());
14757         }
14758     },
14759     
14760     onSearchFieldClick : function(e)
14761     {
14762         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14763             this.onTickableFooterButtonClick(e, false, false);
14764             return;
14765         }
14766         
14767         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14768             return;
14769         }
14770         
14771         this.page = 0;
14772         this.loadNext = false;
14773         this.hasFocus = true;
14774         
14775         if(this.triggerAction == 'all') {
14776             this.doQuery(this.allQuery, true);
14777         } else {
14778             this.doQuery(this.getRawValue());
14779         }
14780     },
14781     
14782     listKeyPress : function(e)
14783     {
14784         //Roo.log('listkeypress');
14785         // scroll to first matching element based on key pres..
14786         if (e.isSpecialKey()) {
14787             return false;
14788         }
14789         var k = String.fromCharCode(e.getKey()).toUpperCase();
14790         //Roo.log(k);
14791         var match  = false;
14792         var csel = this.view.getSelectedNodes();
14793         var cselitem = false;
14794         if (csel.length) {
14795             var ix = this.view.indexOf(csel[0]);
14796             cselitem  = this.store.getAt(ix);
14797             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14798                 cselitem = false;
14799             }
14800             
14801         }
14802         
14803         this.store.each(function(v) { 
14804             if (cselitem) {
14805                 // start at existing selection.
14806                 if (cselitem.id == v.id) {
14807                     cselitem = false;
14808                 }
14809                 return true;
14810             }
14811                 
14812             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14813                 match = this.store.indexOf(v);
14814                 return false;
14815             }
14816             return true;
14817         }, this);
14818         
14819         if (match === false) {
14820             return true; // no more action?
14821         }
14822         // scroll to?
14823         this.view.select(match);
14824         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14825         sn.scrollIntoView(sn.dom.parentNode, false);
14826     },
14827     
14828     onViewScroll : function(e, t){
14829         
14830         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){
14831             return;
14832         }
14833         
14834         this.hasQuery = true;
14835         
14836         this.loading = this.list.select('.loading', true).first();
14837         
14838         if(this.loading === null){
14839             this.list.createChild({
14840                 tag: 'div',
14841                 cls: 'loading roo-select2-more-results roo-select2-active',
14842                 html: 'Loading more results...'
14843             });
14844             
14845             this.loading = this.list.select('.loading', true).first();
14846             
14847             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14848             
14849             this.loading.hide();
14850         }
14851         
14852         this.loading.show();
14853         
14854         var _combo = this;
14855         
14856         this.page++;
14857         this.loadNext = true;
14858         
14859         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14860         
14861         return;
14862     },
14863     
14864     addItem : function(o)
14865     {   
14866         var dv = ''; // display value
14867         
14868         if (this.displayField) {
14869             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14870         } else {
14871             // this is an error condition!!!
14872             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14873         }
14874         
14875         if(!dv.length){
14876             return;
14877         }
14878         
14879         var choice = this.choices.createChild({
14880             tag: 'li',
14881             cls: 'roo-select2-search-choice',
14882             cn: [
14883                 {
14884                     tag: 'div',
14885                     html: dv
14886                 },
14887                 {
14888                     tag: 'a',
14889                     href: '#',
14890                     cls: 'roo-select2-search-choice-close fa fa-times',
14891                     tabindex: '-1'
14892                 }
14893             ]
14894             
14895         }, this.searchField);
14896         
14897         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14898         
14899         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14900         
14901         this.item.push(o);
14902         
14903         this.lastData = o;
14904         
14905         this.syncValue();
14906         
14907         this.inputEl().dom.value = '';
14908         
14909         this.validate();
14910     },
14911     
14912     onRemoveItem : function(e, _self, o)
14913     {
14914         e.preventDefault();
14915         
14916         this.lastItem = Roo.apply([], this.item);
14917         
14918         var index = this.item.indexOf(o.data) * 1;
14919         
14920         if( index < 0){
14921             Roo.log('not this item?!');
14922             return;
14923         }
14924         
14925         this.item.splice(index, 1);
14926         o.item.remove();
14927         
14928         this.syncValue();
14929         
14930         this.fireEvent('remove', this, e);
14931         
14932         this.validate();
14933         
14934     },
14935     
14936     syncValue : function()
14937     {
14938         if(!this.item.length){
14939             this.clearValue();
14940             return;
14941         }
14942             
14943         var value = [];
14944         var _this = this;
14945         Roo.each(this.item, function(i){
14946             if(_this.valueField){
14947                 value.push(i[_this.valueField]);
14948                 return;
14949             }
14950
14951             value.push(i);
14952         });
14953
14954         this.value = value.join(',');
14955
14956         if(this.hiddenField){
14957             this.hiddenField.dom.value = this.value;
14958         }
14959         
14960         this.store.fireEvent("datachanged", this.store);
14961         
14962         this.validate();
14963     },
14964     
14965     clearItem : function()
14966     {
14967         if(!this.multiple){
14968             return;
14969         }
14970         
14971         this.item = [];
14972         
14973         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14974            c.remove();
14975         });
14976         
14977         this.syncValue();
14978         
14979         this.validate();
14980         
14981         if(this.tickable && !Roo.isTouch){
14982             this.view.refresh();
14983         }
14984     },
14985     
14986     inputEl: function ()
14987     {
14988         if(Roo.isIOS && this.useNativeIOS){
14989             return this.el.select('select.roo-ios-select', true).first();
14990         }
14991         
14992         if(Roo.isTouch && this.mobileTouchView){
14993             return this.el.select('input.form-control',true).first();
14994         }
14995         
14996         if(this.tickable){
14997             return this.searchField;
14998         }
14999         
15000         return this.el.select('input.form-control',true).first();
15001     },
15002     
15003     onTickableFooterButtonClick : function(e, btn, el)
15004     {
15005         e.preventDefault();
15006         
15007         this.lastItem = Roo.apply([], this.item);
15008         
15009         if(btn && btn.name == 'cancel'){
15010             this.tickItems = Roo.apply([], this.item);
15011             this.collapse();
15012             return;
15013         }
15014         
15015         this.clearItem();
15016         
15017         var _this = this;
15018         
15019         Roo.each(this.tickItems, function(o){
15020             _this.addItem(o);
15021         });
15022         
15023         this.collapse();
15024         
15025     },
15026     
15027     validate : function()
15028     {
15029         if(this.getVisibilityEl().hasClass('hidden')){
15030             return true;
15031         }
15032         
15033         var v = this.getRawValue();
15034         
15035         if(this.multiple){
15036             v = this.getValue();
15037         }
15038         
15039         if(this.disabled || this.allowBlank || v.length){
15040             this.markValid();
15041             return true;
15042         }
15043         
15044         this.markInvalid();
15045         return false;
15046     },
15047     
15048     tickableInputEl : function()
15049     {
15050         if(!this.tickable || !this.editable){
15051             return this.inputEl();
15052         }
15053         
15054         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15055     },
15056     
15057     
15058     getAutoCreateTouchView : function()
15059     {
15060         var id = Roo.id();
15061         
15062         var cfg = {
15063             cls: 'form-group' //input-group
15064         };
15065         
15066         var input =  {
15067             tag: 'input',
15068             id : id,
15069             type : this.inputType,
15070             cls : 'form-control x-combo-noedit',
15071             autocomplete: 'new-password',
15072             placeholder : this.placeholder || '',
15073             readonly : true
15074         };
15075         
15076         if (this.name) {
15077             input.name = this.name;
15078         }
15079         
15080         if (this.size) {
15081             input.cls += ' input-' + this.size;
15082         }
15083         
15084         if (this.disabled) {
15085             input.disabled = true;
15086         }
15087         
15088         var inputblock = {
15089             cls : '',
15090             cn : [
15091                 input
15092             ]
15093         };
15094         
15095         if(this.before){
15096             inputblock.cls += ' input-group';
15097             
15098             inputblock.cn.unshift({
15099                 tag :'span',
15100                 cls : 'input-group-addon input-group-prepend input-group-text',
15101                 html : this.before
15102             });
15103         }
15104         
15105         if(this.removable && !this.multiple){
15106             inputblock.cls += ' roo-removable';
15107             
15108             inputblock.cn.push({
15109                 tag: 'button',
15110                 html : 'x',
15111                 cls : 'roo-combo-removable-btn close'
15112             });
15113         }
15114
15115         if(this.hasFeedback && !this.allowBlank){
15116             
15117             inputblock.cls += ' has-feedback';
15118             
15119             inputblock.cn.push({
15120                 tag: 'span',
15121                 cls: 'glyphicon form-control-feedback'
15122             });
15123             
15124         }
15125         
15126         if (this.after) {
15127             
15128             inputblock.cls += (this.before) ? '' : ' input-group';
15129             
15130             inputblock.cn.push({
15131                 tag :'span',
15132                 cls : 'input-group-addon input-group-append input-group-text',
15133                 html : this.after
15134             });
15135         }
15136
15137         
15138         var ibwrap = inputblock;
15139         
15140         if(this.multiple){
15141             ibwrap = {
15142                 tag: 'ul',
15143                 cls: 'roo-select2-choices',
15144                 cn:[
15145                     {
15146                         tag: 'li',
15147                         cls: 'roo-select2-search-field',
15148                         cn: [
15149
15150                             inputblock
15151                         ]
15152                     }
15153                 ]
15154             };
15155         
15156             
15157         }
15158         
15159         var combobox = {
15160             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15161             cn: [
15162                 {
15163                     tag: 'input',
15164                     type : 'hidden',
15165                     cls: 'form-hidden-field'
15166                 },
15167                 ibwrap
15168             ]
15169         };
15170         
15171         if(!this.multiple && this.showToggleBtn){
15172             
15173             var caret = {
15174                         tag: 'span',
15175                         cls: 'caret'
15176             };
15177             
15178             if (this.caret != false) {
15179                 caret = {
15180                      tag: 'i',
15181                      cls: 'fa fa-' + this.caret
15182                 };
15183                 
15184             }
15185             
15186             combobox.cn.push({
15187                 tag :'span',
15188                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15189                 cn : [
15190                     caret,
15191                     {
15192                         tag: 'span',
15193                         cls: 'combobox-clear',
15194                         cn  : [
15195                             {
15196                                 tag : 'i',
15197                                 cls: 'icon-remove'
15198                             }
15199                         ]
15200                     }
15201                 ]
15202
15203             })
15204         }
15205         
15206         if(this.multiple){
15207             combobox.cls += ' roo-select2-container-multi';
15208         }
15209         
15210         var align = this.labelAlign || this.parentLabelAlign();
15211         
15212         if (align ==='left' && this.fieldLabel.length) {
15213
15214             cfg.cn = [
15215                 {
15216                    tag : 'i',
15217                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15218                    tooltip : 'This field is required'
15219                 },
15220                 {
15221                     tag: 'label',
15222                     cls : 'control-label col-form-label',
15223                     html : this.fieldLabel
15224
15225                 },
15226                 {
15227                     cls : '', 
15228                     cn: [
15229                         combobox
15230                     ]
15231                 }
15232             ];
15233             
15234             var labelCfg = cfg.cn[1];
15235             var contentCfg = cfg.cn[2];
15236             
15237
15238             if(this.indicatorpos == 'right'){
15239                 cfg.cn = [
15240                     {
15241                         tag: 'label',
15242                         'for' :  id,
15243                         cls : 'control-label col-form-label',
15244                         cn : [
15245                             {
15246                                 tag : 'span',
15247                                 html : this.fieldLabel
15248                             },
15249                             {
15250                                 tag : 'i',
15251                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15252                                 tooltip : 'This field is required'
15253                             }
15254                         ]
15255                     },
15256                     {
15257                         cls : "",
15258                         cn: [
15259                             combobox
15260                         ]
15261                     }
15262
15263                 ];
15264                 
15265                 labelCfg = cfg.cn[0];
15266                 contentCfg = cfg.cn[1];
15267             }
15268             
15269            
15270             
15271             if(this.labelWidth > 12){
15272                 labelCfg.style = "width: " + this.labelWidth + 'px';
15273             }
15274             
15275             if(this.labelWidth < 13 && this.labelmd == 0){
15276                 this.labelmd = this.labelWidth;
15277             }
15278             
15279             if(this.labellg > 0){
15280                 labelCfg.cls += ' col-lg-' + this.labellg;
15281                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15282             }
15283             
15284             if(this.labelmd > 0){
15285                 labelCfg.cls += ' col-md-' + this.labelmd;
15286                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15287             }
15288             
15289             if(this.labelsm > 0){
15290                 labelCfg.cls += ' col-sm-' + this.labelsm;
15291                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15292             }
15293             
15294             if(this.labelxs > 0){
15295                 labelCfg.cls += ' col-xs-' + this.labelxs;
15296                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15297             }
15298                 
15299                 
15300         } else if ( this.fieldLabel.length) {
15301             cfg.cn = [
15302                 {
15303                    tag : 'i',
15304                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15305                    tooltip : 'This field is required'
15306                 },
15307                 {
15308                     tag: 'label',
15309                     cls : 'control-label',
15310                     html : this.fieldLabel
15311
15312                 },
15313                 {
15314                     cls : '', 
15315                     cn: [
15316                         combobox
15317                     ]
15318                 }
15319             ];
15320             
15321             if(this.indicatorpos == 'right'){
15322                 cfg.cn = [
15323                     {
15324                         tag: 'label',
15325                         cls : 'control-label',
15326                         html : this.fieldLabel,
15327                         cn : [
15328                             {
15329                                tag : 'i',
15330                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15331                                tooltip : 'This field is required'
15332                             }
15333                         ]
15334                     },
15335                     {
15336                         cls : '', 
15337                         cn: [
15338                             combobox
15339                         ]
15340                     }
15341                 ];
15342             }
15343         } else {
15344             cfg.cn = combobox;    
15345         }
15346         
15347         
15348         var settings = this;
15349         
15350         ['xs','sm','md','lg'].map(function(size){
15351             if (settings[size]) {
15352                 cfg.cls += ' col-' + size + '-' + settings[size];
15353             }
15354         });
15355         
15356         return cfg;
15357     },
15358     
15359     initTouchView : function()
15360     {
15361         this.renderTouchView();
15362         
15363         this.touchViewEl.on('scroll', function(){
15364             this.el.dom.scrollTop = 0;
15365         }, this);
15366         
15367         this.originalValue = this.getValue();
15368         
15369         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15370         
15371         this.inputEl().on("click", this.showTouchView, this);
15372         if (this.triggerEl) {
15373             this.triggerEl.on("click", this.showTouchView, this);
15374         }
15375         
15376         
15377         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15378         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15379         
15380         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15381         
15382         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15383         this.store.on('load', this.onTouchViewLoad, this);
15384         this.store.on('loadexception', this.onTouchViewLoadException, this);
15385         
15386         if(this.hiddenName){
15387             
15388             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15389             
15390             this.hiddenField.dom.value =
15391                 this.hiddenValue !== undefined ? this.hiddenValue :
15392                 this.value !== undefined ? this.value : '';
15393         
15394             this.el.dom.removeAttribute('name');
15395             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15396         }
15397         
15398         if(this.multiple){
15399             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15400             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15401         }
15402         
15403         if(this.removable && !this.multiple){
15404             var close = this.closeTriggerEl();
15405             if(close){
15406                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15407                 close.on('click', this.removeBtnClick, this, close);
15408             }
15409         }
15410         /*
15411          * fix the bug in Safari iOS8
15412          */
15413         this.inputEl().on("focus", function(e){
15414             document.activeElement.blur();
15415         }, this);
15416         
15417         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15418         
15419         return;
15420         
15421         
15422     },
15423     
15424     renderTouchView : function()
15425     {
15426         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15427         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15428         
15429         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15430         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15431         
15432         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15433         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15434         this.touchViewBodyEl.setStyle('overflow', 'auto');
15435         
15436         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15437         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15438         
15439         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15440         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15441         
15442     },
15443     
15444     showTouchView : function()
15445     {
15446         if(this.disabled){
15447             return;
15448         }
15449         
15450         this.touchViewHeaderEl.hide();
15451
15452         if(this.modalTitle.length){
15453             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15454             this.touchViewHeaderEl.show();
15455         }
15456
15457         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15458         this.touchViewEl.show();
15459
15460         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15461         
15462         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15463         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15464
15465         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15466
15467         if(this.modalTitle.length){
15468             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15469         }
15470         
15471         this.touchViewBodyEl.setHeight(bodyHeight);
15472
15473         if(this.animate){
15474             var _this = this;
15475             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15476         }else{
15477             this.touchViewEl.addClass('in');
15478         }
15479         
15480         if(this._touchViewMask){
15481             Roo.get(document.body).addClass("x-body-masked");
15482             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15483             this._touchViewMask.setStyle('z-index', 10000);
15484             this._touchViewMask.addClass('show');
15485         }
15486         
15487         this.doTouchViewQuery();
15488         
15489     },
15490     
15491     hideTouchView : function()
15492     {
15493         this.touchViewEl.removeClass('in');
15494
15495         if(this.animate){
15496             var _this = this;
15497             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15498         }else{
15499             this.touchViewEl.setStyle('display', 'none');
15500         }
15501         
15502         if(this._touchViewMask){
15503             this._touchViewMask.removeClass('show');
15504             Roo.get(document.body).removeClass("x-body-masked");
15505         }
15506     },
15507     
15508     setTouchViewValue : function()
15509     {
15510         if(this.multiple){
15511             this.clearItem();
15512         
15513             var _this = this;
15514
15515             Roo.each(this.tickItems, function(o){
15516                 this.addItem(o);
15517             }, this);
15518         }
15519         
15520         this.hideTouchView();
15521     },
15522     
15523     doTouchViewQuery : function()
15524     {
15525         var qe = {
15526             query: '',
15527             forceAll: true,
15528             combo: this,
15529             cancel:false
15530         };
15531         
15532         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15533             return false;
15534         }
15535         
15536         if(!this.alwaysQuery || this.mode == 'local'){
15537             this.onTouchViewLoad();
15538             return;
15539         }
15540         
15541         this.store.load();
15542     },
15543     
15544     onTouchViewBeforeLoad : function(combo,opts)
15545     {
15546         return;
15547     },
15548
15549     // private
15550     onTouchViewLoad : function()
15551     {
15552         if(this.store.getCount() < 1){
15553             this.onTouchViewEmptyResults();
15554             return;
15555         }
15556         
15557         this.clearTouchView();
15558         
15559         var rawValue = this.getRawValue();
15560         
15561         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15562         
15563         this.tickItems = [];
15564         
15565         this.store.data.each(function(d, rowIndex){
15566             var row = this.touchViewListGroup.createChild(template);
15567             
15568             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15569                 row.addClass(d.data.cls);
15570             }
15571             
15572             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15573                 var cfg = {
15574                     data : d.data,
15575                     html : d.data[this.displayField]
15576                 };
15577                 
15578                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15579                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15580                 }
15581             }
15582             row.removeClass('selected');
15583             if(!this.multiple && this.valueField &&
15584                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15585             {
15586                 // radio buttons..
15587                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15588                 row.addClass('selected');
15589             }
15590             
15591             if(this.multiple && this.valueField &&
15592                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15593             {
15594                 
15595                 // checkboxes...
15596                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15597                 this.tickItems.push(d.data);
15598             }
15599             
15600             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15601             
15602         }, this);
15603         
15604         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15605         
15606         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15607
15608         if(this.modalTitle.length){
15609             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15610         }
15611
15612         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15613         
15614         if(this.mobile_restrict_height && listHeight < bodyHeight){
15615             this.touchViewBodyEl.setHeight(listHeight);
15616         }
15617         
15618         var _this = this;
15619         
15620         if(firstChecked && listHeight > bodyHeight){
15621             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15622         }
15623         
15624     },
15625     
15626     onTouchViewLoadException : function()
15627     {
15628         this.hideTouchView();
15629     },
15630     
15631     onTouchViewEmptyResults : function()
15632     {
15633         this.clearTouchView();
15634         
15635         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15636         
15637         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15638         
15639     },
15640     
15641     clearTouchView : function()
15642     {
15643         this.touchViewListGroup.dom.innerHTML = '';
15644     },
15645     
15646     onTouchViewClick : function(e, el, o)
15647     {
15648         e.preventDefault();
15649         
15650         var row = o.row;
15651         var rowIndex = o.rowIndex;
15652         
15653         var r = this.store.getAt(rowIndex);
15654         
15655         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15656             
15657             if(!this.multiple){
15658                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15659                     c.dom.removeAttribute('checked');
15660                 }, this);
15661
15662                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15663
15664                 this.setFromData(r.data);
15665
15666                 var close = this.closeTriggerEl();
15667
15668                 if(close){
15669                     close.show();
15670                 }
15671
15672                 this.hideTouchView();
15673
15674                 this.fireEvent('select', this, r, rowIndex);
15675
15676                 return;
15677             }
15678
15679             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15680                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15681                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15682                 return;
15683             }
15684
15685             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15686             this.addItem(r.data);
15687             this.tickItems.push(r.data);
15688         }
15689     },
15690     
15691     getAutoCreateNativeIOS : function()
15692     {
15693         var cfg = {
15694             cls: 'form-group' //input-group,
15695         };
15696         
15697         var combobox =  {
15698             tag: 'select',
15699             cls : 'roo-ios-select'
15700         };
15701         
15702         if (this.name) {
15703             combobox.name = this.name;
15704         }
15705         
15706         if (this.disabled) {
15707             combobox.disabled = true;
15708         }
15709         
15710         var settings = this;
15711         
15712         ['xs','sm','md','lg'].map(function(size){
15713             if (settings[size]) {
15714                 cfg.cls += ' col-' + size + '-' + settings[size];
15715             }
15716         });
15717         
15718         cfg.cn = combobox;
15719         
15720         return cfg;
15721         
15722     },
15723     
15724     initIOSView : function()
15725     {
15726         this.store.on('load', this.onIOSViewLoad, this);
15727         
15728         return;
15729     },
15730     
15731     onIOSViewLoad : function()
15732     {
15733         if(this.store.getCount() < 1){
15734             return;
15735         }
15736         
15737         this.clearIOSView();
15738         
15739         if(this.allowBlank) {
15740             
15741             var default_text = '-- SELECT --';
15742             
15743             if(this.placeholder.length){
15744                 default_text = this.placeholder;
15745             }
15746             
15747             if(this.emptyTitle.length){
15748                 default_text += ' - ' + this.emptyTitle + ' -';
15749             }
15750             
15751             var opt = this.inputEl().createChild({
15752                 tag: 'option',
15753                 value : 0,
15754                 html : default_text
15755             });
15756             
15757             var o = {};
15758             o[this.valueField] = 0;
15759             o[this.displayField] = default_text;
15760             
15761             this.ios_options.push({
15762                 data : o,
15763                 el : opt
15764             });
15765             
15766         }
15767         
15768         this.store.data.each(function(d, rowIndex){
15769             
15770             var html = '';
15771             
15772             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15773                 html = d.data[this.displayField];
15774             }
15775             
15776             var value = '';
15777             
15778             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15779                 value = d.data[this.valueField];
15780             }
15781             
15782             var option = {
15783                 tag: 'option',
15784                 value : value,
15785                 html : html
15786             };
15787             
15788             if(this.value == d.data[this.valueField]){
15789                 option['selected'] = true;
15790             }
15791             
15792             var opt = this.inputEl().createChild(option);
15793             
15794             this.ios_options.push({
15795                 data : d.data,
15796                 el : opt
15797             });
15798             
15799         }, this);
15800         
15801         this.inputEl().on('change', function(){
15802            this.fireEvent('select', this);
15803         }, this);
15804         
15805     },
15806     
15807     clearIOSView: function()
15808     {
15809         this.inputEl().dom.innerHTML = '';
15810         
15811         this.ios_options = [];
15812     },
15813     
15814     setIOSValue: function(v)
15815     {
15816         this.value = v;
15817         
15818         if(!this.ios_options){
15819             return;
15820         }
15821         
15822         Roo.each(this.ios_options, function(opts){
15823            
15824            opts.el.dom.removeAttribute('selected');
15825            
15826            if(opts.data[this.valueField] != v){
15827                return;
15828            }
15829            
15830            opts.el.dom.setAttribute('selected', true);
15831            
15832         }, this);
15833     }
15834
15835     /** 
15836     * @cfg {Boolean} grow 
15837     * @hide 
15838     */
15839     /** 
15840     * @cfg {Number} growMin 
15841     * @hide 
15842     */
15843     /** 
15844     * @cfg {Number} growMax 
15845     * @hide 
15846     */
15847     /**
15848      * @hide
15849      * @method autoSize
15850      */
15851 });
15852
15853 Roo.apply(Roo.bootstrap.ComboBox,  {
15854     
15855     header : {
15856         tag: 'div',
15857         cls: 'modal-header',
15858         cn: [
15859             {
15860                 tag: 'h4',
15861                 cls: 'modal-title'
15862             }
15863         ]
15864     },
15865     
15866     body : {
15867         tag: 'div',
15868         cls: 'modal-body',
15869         cn: [
15870             {
15871                 tag: 'ul',
15872                 cls: 'list-group'
15873             }
15874         ]
15875     },
15876     
15877     listItemRadio : {
15878         tag: 'li',
15879         cls: 'list-group-item',
15880         cn: [
15881             {
15882                 tag: 'span',
15883                 cls: 'roo-combobox-list-group-item-value'
15884             },
15885             {
15886                 tag: 'div',
15887                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15888                 cn: [
15889                     {
15890                         tag: 'input',
15891                         type: 'radio'
15892                     },
15893                     {
15894                         tag: 'label'
15895                     }
15896                 ]
15897             }
15898         ]
15899     },
15900     
15901     listItemCheckbox : {
15902         tag: 'li',
15903         cls: 'list-group-item',
15904         cn: [
15905             {
15906                 tag: 'span',
15907                 cls: 'roo-combobox-list-group-item-value'
15908             },
15909             {
15910                 tag: 'div',
15911                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15912                 cn: [
15913                     {
15914                         tag: 'input',
15915                         type: 'checkbox'
15916                     },
15917                     {
15918                         tag: 'label'
15919                     }
15920                 ]
15921             }
15922         ]
15923     },
15924     
15925     emptyResult : {
15926         tag: 'div',
15927         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15928     },
15929     
15930     footer : {
15931         tag: 'div',
15932         cls: 'modal-footer',
15933         cn: [
15934             {
15935                 tag: 'div',
15936                 cls: 'row',
15937                 cn: [
15938                     {
15939                         tag: 'div',
15940                         cls: 'col-xs-6 text-left',
15941                         cn: {
15942                             tag: 'button',
15943                             cls: 'btn btn-danger roo-touch-view-cancel',
15944                             html: 'Cancel'
15945                         }
15946                     },
15947                     {
15948                         tag: 'div',
15949                         cls: 'col-xs-6 text-right',
15950                         cn: {
15951                             tag: 'button',
15952                             cls: 'btn btn-success roo-touch-view-ok',
15953                             html: 'OK'
15954                         }
15955                     }
15956                 ]
15957             }
15958         ]
15959         
15960     }
15961 });
15962
15963 Roo.apply(Roo.bootstrap.ComboBox,  {
15964     
15965     touchViewTemplate : {
15966         tag: 'div',
15967         cls: 'modal fade roo-combobox-touch-view',
15968         cn: [
15969             {
15970                 tag: 'div',
15971                 cls: 'modal-dialog',
15972                 style : 'position:fixed', // we have to fix position....
15973                 cn: [
15974                     {
15975                         tag: 'div',
15976                         cls: 'modal-content',
15977                         cn: [
15978                             Roo.bootstrap.ComboBox.header,
15979                             Roo.bootstrap.ComboBox.body,
15980                             Roo.bootstrap.ComboBox.footer
15981                         ]
15982                     }
15983                 ]
15984             }
15985         ]
15986     }
15987 });/*
15988  * Based on:
15989  * Ext JS Library 1.1.1
15990  * Copyright(c) 2006-2007, Ext JS, LLC.
15991  *
15992  * Originally Released Under LGPL - original licence link has changed is not relivant.
15993  *
15994  * Fork - LGPL
15995  * <script type="text/javascript">
15996  */
15997
15998 /**
15999  * @class Roo.View
16000  * @extends Roo.util.Observable
16001  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16002  * This class also supports single and multi selection modes. <br>
16003  * Create a data model bound view:
16004  <pre><code>
16005  var store = new Roo.data.Store(...);
16006
16007  var view = new Roo.View({
16008     el : "my-element",
16009     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16010  
16011     singleSelect: true,
16012     selectedClass: "ydataview-selected",
16013     store: store
16014  });
16015
16016  // listen for node click?
16017  view.on("click", function(vw, index, node, e){
16018  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16019  });
16020
16021  // load XML data
16022  dataModel.load("foobar.xml");
16023  </code></pre>
16024  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16025  * <br><br>
16026  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16027  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16028  * 
16029  * Note: old style constructor is still suported (container, template, config)
16030  * 
16031  * @constructor
16032  * Create a new View
16033  * @param {Object} config The config object
16034  * 
16035  */
16036 Roo.View = function(config, depreciated_tpl, depreciated_config){
16037     
16038     this.parent = false;
16039     
16040     if (typeof(depreciated_tpl) == 'undefined') {
16041         // new way.. - universal constructor.
16042         Roo.apply(this, config);
16043         this.el  = Roo.get(this.el);
16044     } else {
16045         // old format..
16046         this.el  = Roo.get(config);
16047         this.tpl = depreciated_tpl;
16048         Roo.apply(this, depreciated_config);
16049     }
16050     this.wrapEl  = this.el.wrap().wrap();
16051     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16052     
16053     
16054     if(typeof(this.tpl) == "string"){
16055         this.tpl = new Roo.Template(this.tpl);
16056     } else {
16057         // support xtype ctors..
16058         this.tpl = new Roo.factory(this.tpl, Roo);
16059     }
16060     
16061     
16062     this.tpl.compile();
16063     
16064     /** @private */
16065     this.addEvents({
16066         /**
16067          * @event beforeclick
16068          * Fires before a click is processed. Returns false to cancel the default action.
16069          * @param {Roo.View} this
16070          * @param {Number} index The index of the target node
16071          * @param {HTMLElement} node The target node
16072          * @param {Roo.EventObject} e The raw event object
16073          */
16074             "beforeclick" : true,
16075         /**
16076          * @event click
16077          * Fires when a template node is clicked.
16078          * @param {Roo.View} this
16079          * @param {Number} index The index of the target node
16080          * @param {HTMLElement} node The target node
16081          * @param {Roo.EventObject} e The raw event object
16082          */
16083             "click" : true,
16084         /**
16085          * @event dblclick
16086          * Fires when a template node is double clicked.
16087          * @param {Roo.View} this
16088          * @param {Number} index The index of the target node
16089          * @param {HTMLElement} node The target node
16090          * @param {Roo.EventObject} e The raw event object
16091          */
16092             "dblclick" : true,
16093         /**
16094          * @event contextmenu
16095          * Fires when a template node is right clicked.
16096          * @param {Roo.View} this
16097          * @param {Number} index The index of the target node
16098          * @param {HTMLElement} node The target node
16099          * @param {Roo.EventObject} e The raw event object
16100          */
16101             "contextmenu" : true,
16102         /**
16103          * @event selectionchange
16104          * Fires when the selected nodes change.
16105          * @param {Roo.View} this
16106          * @param {Array} selections Array of the selected nodes
16107          */
16108             "selectionchange" : true,
16109     
16110         /**
16111          * @event beforeselect
16112          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16113          * @param {Roo.View} this
16114          * @param {HTMLElement} node The node to be selected
16115          * @param {Array} selections Array of currently selected nodes
16116          */
16117             "beforeselect" : true,
16118         /**
16119          * @event preparedata
16120          * Fires on every row to render, to allow you to change the data.
16121          * @param {Roo.View} this
16122          * @param {Object} data to be rendered (change this)
16123          */
16124           "preparedata" : true
16125           
16126           
16127         });
16128
16129
16130
16131     this.el.on({
16132         "click": this.onClick,
16133         "dblclick": this.onDblClick,
16134         "contextmenu": this.onContextMenu,
16135         scope:this
16136     });
16137
16138     this.selections = [];
16139     this.nodes = [];
16140     this.cmp = new Roo.CompositeElementLite([]);
16141     if(this.store){
16142         this.store = Roo.factory(this.store, Roo.data);
16143         this.setStore(this.store, true);
16144     }
16145     
16146     if ( this.footer && this.footer.xtype) {
16147            
16148          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16149         
16150         this.footer.dataSource = this.store;
16151         this.footer.container = fctr;
16152         this.footer = Roo.factory(this.footer, Roo);
16153         fctr.insertFirst(this.el);
16154         
16155         // this is a bit insane - as the paging toolbar seems to detach the el..
16156 //        dom.parentNode.parentNode.parentNode
16157          // they get detached?
16158     }
16159     
16160     
16161     Roo.View.superclass.constructor.call(this);
16162     
16163     
16164 };
16165
16166 Roo.extend(Roo.View, Roo.util.Observable, {
16167     
16168      /**
16169      * @cfg {Roo.data.Store} store Data store to load data from.
16170      */
16171     store : false,
16172     
16173     /**
16174      * @cfg {String|Roo.Element} el The container element.
16175      */
16176     el : '',
16177     
16178     /**
16179      * @cfg {String|Roo.Template} tpl The template used by this View 
16180      */
16181     tpl : false,
16182     /**
16183      * @cfg {String} dataName the named area of the template to use as the data area
16184      *                          Works with domtemplates roo-name="name"
16185      */
16186     dataName: false,
16187     /**
16188      * @cfg {String} selectedClass The css class to add to selected nodes
16189      */
16190     selectedClass : "x-view-selected",
16191      /**
16192      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16193      */
16194     emptyText : "",
16195     
16196     /**
16197      * @cfg {String} text to display on mask (default Loading)
16198      */
16199     mask : false,
16200     /**
16201      * @cfg {Boolean} multiSelect Allow multiple selection
16202      */
16203     multiSelect : false,
16204     /**
16205      * @cfg {Boolean} singleSelect Allow single selection
16206      */
16207     singleSelect:  false,
16208     
16209     /**
16210      * @cfg {Boolean} toggleSelect - selecting 
16211      */
16212     toggleSelect : false,
16213     
16214     /**
16215      * @cfg {Boolean} tickable - selecting 
16216      */
16217     tickable : false,
16218     
16219     /**
16220      * Returns the element this view is bound to.
16221      * @return {Roo.Element}
16222      */
16223     getEl : function(){
16224         return this.wrapEl;
16225     },
16226     
16227     
16228
16229     /**
16230      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16231      */
16232     refresh : function(){
16233         //Roo.log('refresh');
16234         var t = this.tpl;
16235         
16236         // if we are using something like 'domtemplate', then
16237         // the what gets used is:
16238         // t.applySubtemplate(NAME, data, wrapping data..)
16239         // the outer template then get' applied with
16240         //     the store 'extra data'
16241         // and the body get's added to the
16242         //      roo-name="data" node?
16243         //      <span class='roo-tpl-{name}'></span> ?????
16244         
16245         
16246         
16247         this.clearSelections();
16248         this.el.update("");
16249         var html = [];
16250         var records = this.store.getRange();
16251         if(records.length < 1) {
16252             
16253             // is this valid??  = should it render a template??
16254             
16255             this.el.update(this.emptyText);
16256             return;
16257         }
16258         var el = this.el;
16259         if (this.dataName) {
16260             this.el.update(t.apply(this.store.meta)); //????
16261             el = this.el.child('.roo-tpl-' + this.dataName);
16262         }
16263         
16264         for(var i = 0, len = records.length; i < len; i++){
16265             var data = this.prepareData(records[i].data, i, records[i]);
16266             this.fireEvent("preparedata", this, data, i, records[i]);
16267             
16268             var d = Roo.apply({}, data);
16269             
16270             if(this.tickable){
16271                 Roo.apply(d, {'roo-id' : Roo.id()});
16272                 
16273                 var _this = this;
16274             
16275                 Roo.each(this.parent.item, function(item){
16276                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16277                         return;
16278                     }
16279                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16280                 });
16281             }
16282             
16283             html[html.length] = Roo.util.Format.trim(
16284                 this.dataName ?
16285                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16286                     t.apply(d)
16287             );
16288         }
16289         
16290         
16291         
16292         el.update(html.join(""));
16293         this.nodes = el.dom.childNodes;
16294         this.updateIndexes(0);
16295     },
16296     
16297
16298     /**
16299      * Function to override to reformat the data that is sent to
16300      * the template for each node.
16301      * DEPRICATED - use the preparedata event handler.
16302      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16303      * a JSON object for an UpdateManager bound view).
16304      */
16305     prepareData : function(data, index, record)
16306     {
16307         this.fireEvent("preparedata", this, data, index, record);
16308         return data;
16309     },
16310
16311     onUpdate : function(ds, record){
16312         // Roo.log('on update');   
16313         this.clearSelections();
16314         var index = this.store.indexOf(record);
16315         var n = this.nodes[index];
16316         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16317         n.parentNode.removeChild(n);
16318         this.updateIndexes(index, index);
16319     },
16320
16321     
16322     
16323 // --------- FIXME     
16324     onAdd : function(ds, records, index)
16325     {
16326         //Roo.log(['on Add', ds, records, index] );        
16327         this.clearSelections();
16328         if(this.nodes.length == 0){
16329             this.refresh();
16330             return;
16331         }
16332         var n = this.nodes[index];
16333         for(var i = 0, len = records.length; i < len; i++){
16334             var d = this.prepareData(records[i].data, i, records[i]);
16335             if(n){
16336                 this.tpl.insertBefore(n, d);
16337             }else{
16338                 
16339                 this.tpl.append(this.el, d);
16340             }
16341         }
16342         this.updateIndexes(index);
16343     },
16344
16345     onRemove : function(ds, record, index){
16346        // Roo.log('onRemove');
16347         this.clearSelections();
16348         var el = this.dataName  ?
16349             this.el.child('.roo-tpl-' + this.dataName) :
16350             this.el; 
16351         
16352         el.dom.removeChild(this.nodes[index]);
16353         this.updateIndexes(index);
16354     },
16355
16356     /**
16357      * Refresh an individual node.
16358      * @param {Number} index
16359      */
16360     refreshNode : function(index){
16361         this.onUpdate(this.store, this.store.getAt(index));
16362     },
16363
16364     updateIndexes : function(startIndex, endIndex){
16365         var ns = this.nodes;
16366         startIndex = startIndex || 0;
16367         endIndex = endIndex || ns.length - 1;
16368         for(var i = startIndex; i <= endIndex; i++){
16369             ns[i].nodeIndex = i;
16370         }
16371     },
16372
16373     /**
16374      * Changes the data store this view uses and refresh the view.
16375      * @param {Store} store
16376      */
16377     setStore : function(store, initial){
16378         if(!initial && this.store){
16379             this.store.un("datachanged", this.refresh);
16380             this.store.un("add", this.onAdd);
16381             this.store.un("remove", this.onRemove);
16382             this.store.un("update", this.onUpdate);
16383             this.store.un("clear", this.refresh);
16384             this.store.un("beforeload", this.onBeforeLoad);
16385             this.store.un("load", this.onLoad);
16386             this.store.un("loadexception", this.onLoad);
16387         }
16388         if(store){
16389           
16390             store.on("datachanged", this.refresh, this);
16391             store.on("add", this.onAdd, this);
16392             store.on("remove", this.onRemove, this);
16393             store.on("update", this.onUpdate, this);
16394             store.on("clear", this.refresh, this);
16395             store.on("beforeload", this.onBeforeLoad, this);
16396             store.on("load", this.onLoad, this);
16397             store.on("loadexception", this.onLoad, this);
16398         }
16399         
16400         if(store){
16401             this.refresh();
16402         }
16403     },
16404     /**
16405      * onbeforeLoad - masks the loading area.
16406      *
16407      */
16408     onBeforeLoad : function(store,opts)
16409     {
16410          //Roo.log('onBeforeLoad');   
16411         if (!opts.add) {
16412             this.el.update("");
16413         }
16414         this.el.mask(this.mask ? this.mask : "Loading" ); 
16415     },
16416     onLoad : function ()
16417     {
16418         this.el.unmask();
16419     },
16420     
16421
16422     /**
16423      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16424      * @param {HTMLElement} node
16425      * @return {HTMLElement} The template node
16426      */
16427     findItemFromChild : function(node){
16428         var el = this.dataName  ?
16429             this.el.child('.roo-tpl-' + this.dataName,true) :
16430             this.el.dom; 
16431         
16432         if(!node || node.parentNode == el){
16433                     return node;
16434             }
16435             var p = node.parentNode;
16436             while(p && p != el){
16437             if(p.parentNode == el){
16438                 return p;
16439             }
16440             p = p.parentNode;
16441         }
16442             return null;
16443     },
16444
16445     /** @ignore */
16446     onClick : function(e){
16447         var item = this.findItemFromChild(e.getTarget());
16448         if(item){
16449             var index = this.indexOf(item);
16450             if(this.onItemClick(item, index, e) !== false){
16451                 this.fireEvent("click", this, index, item, e);
16452             }
16453         }else{
16454             this.clearSelections();
16455         }
16456     },
16457
16458     /** @ignore */
16459     onContextMenu : function(e){
16460         var item = this.findItemFromChild(e.getTarget());
16461         if(item){
16462             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16463         }
16464     },
16465
16466     /** @ignore */
16467     onDblClick : function(e){
16468         var item = this.findItemFromChild(e.getTarget());
16469         if(item){
16470             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16471         }
16472     },
16473
16474     onItemClick : function(item, index, e)
16475     {
16476         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16477             return false;
16478         }
16479         if (this.toggleSelect) {
16480             var m = this.isSelected(item) ? 'unselect' : 'select';
16481             //Roo.log(m);
16482             var _t = this;
16483             _t[m](item, true, false);
16484             return true;
16485         }
16486         if(this.multiSelect || this.singleSelect){
16487             if(this.multiSelect && e.shiftKey && this.lastSelection){
16488                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16489             }else{
16490                 this.select(item, this.multiSelect && e.ctrlKey);
16491                 this.lastSelection = item;
16492             }
16493             
16494             if(!this.tickable){
16495                 e.preventDefault();
16496             }
16497             
16498         }
16499         return true;
16500     },
16501
16502     /**
16503      * Get the number of selected nodes.
16504      * @return {Number}
16505      */
16506     getSelectionCount : function(){
16507         return this.selections.length;
16508     },
16509
16510     /**
16511      * Get the currently selected nodes.
16512      * @return {Array} An array of HTMLElements
16513      */
16514     getSelectedNodes : function(){
16515         return this.selections;
16516     },
16517
16518     /**
16519      * Get the indexes of the selected nodes.
16520      * @return {Array}
16521      */
16522     getSelectedIndexes : function(){
16523         var indexes = [], s = this.selections;
16524         for(var i = 0, len = s.length; i < len; i++){
16525             indexes.push(s[i].nodeIndex);
16526         }
16527         return indexes;
16528     },
16529
16530     /**
16531      * Clear all selections
16532      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16533      */
16534     clearSelections : function(suppressEvent){
16535         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16536             this.cmp.elements = this.selections;
16537             this.cmp.removeClass(this.selectedClass);
16538             this.selections = [];
16539             if(!suppressEvent){
16540                 this.fireEvent("selectionchange", this, this.selections);
16541             }
16542         }
16543     },
16544
16545     /**
16546      * Returns true if the passed node is selected
16547      * @param {HTMLElement/Number} node The node or node index
16548      * @return {Boolean}
16549      */
16550     isSelected : function(node){
16551         var s = this.selections;
16552         if(s.length < 1){
16553             return false;
16554         }
16555         node = this.getNode(node);
16556         return s.indexOf(node) !== -1;
16557     },
16558
16559     /**
16560      * Selects nodes.
16561      * @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
16562      * @param {Boolean} keepExisting (optional) true to keep existing selections
16563      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16564      */
16565     select : function(nodeInfo, keepExisting, suppressEvent){
16566         if(nodeInfo instanceof Array){
16567             if(!keepExisting){
16568                 this.clearSelections(true);
16569             }
16570             for(var i = 0, len = nodeInfo.length; i < len; i++){
16571                 this.select(nodeInfo[i], true, true);
16572             }
16573             return;
16574         } 
16575         var node = this.getNode(nodeInfo);
16576         if(!node || this.isSelected(node)){
16577             return; // already selected.
16578         }
16579         if(!keepExisting){
16580             this.clearSelections(true);
16581         }
16582         
16583         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16584             Roo.fly(node).addClass(this.selectedClass);
16585             this.selections.push(node);
16586             if(!suppressEvent){
16587                 this.fireEvent("selectionchange", this, this.selections);
16588             }
16589         }
16590         
16591         
16592     },
16593       /**
16594      * Unselects nodes.
16595      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16596      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16597      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16598      */
16599     unselect : function(nodeInfo, keepExisting, suppressEvent)
16600     {
16601         if(nodeInfo instanceof Array){
16602             Roo.each(this.selections, function(s) {
16603                 this.unselect(s, nodeInfo);
16604             }, this);
16605             return;
16606         }
16607         var node = this.getNode(nodeInfo);
16608         if(!node || !this.isSelected(node)){
16609             //Roo.log("not selected");
16610             return; // not selected.
16611         }
16612         // fireevent???
16613         var ns = [];
16614         Roo.each(this.selections, function(s) {
16615             if (s == node ) {
16616                 Roo.fly(node).removeClass(this.selectedClass);
16617
16618                 return;
16619             }
16620             ns.push(s);
16621         },this);
16622         
16623         this.selections= ns;
16624         this.fireEvent("selectionchange", this, this.selections);
16625     },
16626
16627     /**
16628      * Gets a template node.
16629      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16630      * @return {HTMLElement} The node or null if it wasn't found
16631      */
16632     getNode : function(nodeInfo){
16633         if(typeof nodeInfo == "string"){
16634             return document.getElementById(nodeInfo);
16635         }else if(typeof nodeInfo == "number"){
16636             return this.nodes[nodeInfo];
16637         }
16638         return nodeInfo;
16639     },
16640
16641     /**
16642      * Gets a range template nodes.
16643      * @param {Number} startIndex
16644      * @param {Number} endIndex
16645      * @return {Array} An array of nodes
16646      */
16647     getNodes : function(start, end){
16648         var ns = this.nodes;
16649         start = start || 0;
16650         end = typeof end == "undefined" ? ns.length - 1 : end;
16651         var nodes = [];
16652         if(start <= end){
16653             for(var i = start; i <= end; i++){
16654                 nodes.push(ns[i]);
16655             }
16656         } else{
16657             for(var i = start; i >= end; i--){
16658                 nodes.push(ns[i]);
16659             }
16660         }
16661         return nodes;
16662     },
16663
16664     /**
16665      * Finds the index of the passed node
16666      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16667      * @return {Number} The index of the node or -1
16668      */
16669     indexOf : function(node){
16670         node = this.getNode(node);
16671         if(typeof node.nodeIndex == "number"){
16672             return node.nodeIndex;
16673         }
16674         var ns = this.nodes;
16675         for(var i = 0, len = ns.length; i < len; i++){
16676             if(ns[i] == node){
16677                 return i;
16678             }
16679         }
16680         return -1;
16681     }
16682 });
16683 /*
16684  * - LGPL
16685  *
16686  * based on jquery fullcalendar
16687  * 
16688  */
16689
16690 Roo.bootstrap = Roo.bootstrap || {};
16691 /**
16692  * @class Roo.bootstrap.Calendar
16693  * @extends Roo.bootstrap.Component
16694  * Bootstrap Calendar class
16695  * @cfg {Boolean} loadMask (true|false) default false
16696  * @cfg {Object} header generate the user specific header of the calendar, default false
16697
16698  * @constructor
16699  * Create a new Container
16700  * @param {Object} config The config object
16701  */
16702
16703
16704
16705 Roo.bootstrap.Calendar = function(config){
16706     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16707      this.addEvents({
16708         /**
16709              * @event select
16710              * Fires when a date is selected
16711              * @param {DatePicker} this
16712              * @param {Date} date The selected date
16713              */
16714         'select': true,
16715         /**
16716              * @event monthchange
16717              * Fires when the displayed month changes 
16718              * @param {DatePicker} this
16719              * @param {Date} date The selected month
16720              */
16721         'monthchange': true,
16722         /**
16723              * @event evententer
16724              * Fires when mouse over an event
16725              * @param {Calendar} this
16726              * @param {event} Event
16727              */
16728         'evententer': true,
16729         /**
16730              * @event eventleave
16731              * Fires when the mouse leaves an
16732              * @param {Calendar} this
16733              * @param {event}
16734              */
16735         'eventleave': true,
16736         /**
16737              * @event eventclick
16738              * Fires when the mouse click an
16739              * @param {Calendar} this
16740              * @param {event}
16741              */
16742         'eventclick': true
16743         
16744     });
16745
16746 };
16747
16748 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16749     
16750      /**
16751      * @cfg {Number} startDay
16752      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16753      */
16754     startDay : 0,
16755     
16756     loadMask : false,
16757     
16758     header : false,
16759       
16760     getAutoCreate : function(){
16761         
16762         
16763         var fc_button = function(name, corner, style, content ) {
16764             return Roo.apply({},{
16765                 tag : 'span',
16766                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16767                          (corner.length ?
16768                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16769                             ''
16770                         ),
16771                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16772                 unselectable: 'on'
16773             });
16774         };
16775         
16776         var header = {};
16777         
16778         if(!this.header){
16779             header = {
16780                 tag : 'table',
16781                 cls : 'fc-header',
16782                 style : 'width:100%',
16783                 cn : [
16784                     {
16785                         tag: 'tr',
16786                         cn : [
16787                             {
16788                                 tag : 'td',
16789                                 cls : 'fc-header-left',
16790                                 cn : [
16791                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16792                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16793                                     { tag: 'span', cls: 'fc-header-space' },
16794                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16795
16796
16797                                 ]
16798                             },
16799
16800                             {
16801                                 tag : 'td',
16802                                 cls : 'fc-header-center',
16803                                 cn : [
16804                                     {
16805                                         tag: 'span',
16806                                         cls: 'fc-header-title',
16807                                         cn : {
16808                                             tag: 'H2',
16809                                             html : 'month / year'
16810                                         }
16811                                     }
16812
16813                                 ]
16814                             },
16815                             {
16816                                 tag : 'td',
16817                                 cls : 'fc-header-right',
16818                                 cn : [
16819                               /*      fc_button('month', 'left', '', 'month' ),
16820                                     fc_button('week', '', '', 'week' ),
16821                                     fc_button('day', 'right', '', 'day' )
16822                                 */    
16823
16824                                 ]
16825                             }
16826
16827                         ]
16828                     }
16829                 ]
16830             };
16831         }
16832         
16833         header = this.header;
16834         
16835        
16836         var cal_heads = function() {
16837             var ret = [];
16838             // fixme - handle this.
16839             
16840             for (var i =0; i < Date.dayNames.length; i++) {
16841                 var d = Date.dayNames[i];
16842                 ret.push({
16843                     tag: 'th',
16844                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16845                     html : d.substring(0,3)
16846                 });
16847                 
16848             }
16849             ret[0].cls += ' fc-first';
16850             ret[6].cls += ' fc-last';
16851             return ret;
16852         };
16853         var cal_cell = function(n) {
16854             return  {
16855                 tag: 'td',
16856                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16857                 cn : [
16858                     {
16859                         cn : [
16860                             {
16861                                 cls: 'fc-day-number',
16862                                 html: 'D'
16863                             },
16864                             {
16865                                 cls: 'fc-day-content',
16866                              
16867                                 cn : [
16868                                      {
16869                                         style: 'position: relative;' // height: 17px;
16870                                     }
16871                                 ]
16872                             }
16873                             
16874                             
16875                         ]
16876                     }
16877                 ]
16878                 
16879             }
16880         };
16881         var cal_rows = function() {
16882             
16883             var ret = [];
16884             for (var r = 0; r < 6; r++) {
16885                 var row= {
16886                     tag : 'tr',
16887                     cls : 'fc-week',
16888                     cn : []
16889                 };
16890                 
16891                 for (var i =0; i < Date.dayNames.length; i++) {
16892                     var d = Date.dayNames[i];
16893                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16894
16895                 }
16896                 row.cn[0].cls+=' fc-first';
16897                 row.cn[0].cn[0].style = 'min-height:90px';
16898                 row.cn[6].cls+=' fc-last';
16899                 ret.push(row);
16900                 
16901             }
16902             ret[0].cls += ' fc-first';
16903             ret[4].cls += ' fc-prev-last';
16904             ret[5].cls += ' fc-last';
16905             return ret;
16906             
16907         };
16908         
16909         var cal_table = {
16910             tag: 'table',
16911             cls: 'fc-border-separate',
16912             style : 'width:100%',
16913             cellspacing  : 0,
16914             cn : [
16915                 { 
16916                     tag: 'thead',
16917                     cn : [
16918                         { 
16919                             tag: 'tr',
16920                             cls : 'fc-first fc-last',
16921                             cn : cal_heads()
16922                         }
16923                     ]
16924                 },
16925                 { 
16926                     tag: 'tbody',
16927                     cn : cal_rows()
16928                 }
16929                   
16930             ]
16931         };
16932          
16933          var cfg = {
16934             cls : 'fc fc-ltr',
16935             cn : [
16936                 header,
16937                 {
16938                     cls : 'fc-content',
16939                     style : "position: relative;",
16940                     cn : [
16941                         {
16942                             cls : 'fc-view fc-view-month fc-grid',
16943                             style : 'position: relative',
16944                             unselectable : 'on',
16945                             cn : [
16946                                 {
16947                                     cls : 'fc-event-container',
16948                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16949                                 },
16950                                 cal_table
16951                             ]
16952                         }
16953                     ]
16954     
16955                 }
16956            ] 
16957             
16958         };
16959         
16960          
16961         
16962         return cfg;
16963     },
16964     
16965     
16966     initEvents : function()
16967     {
16968         if(!this.store){
16969             throw "can not find store for calendar";
16970         }
16971         
16972         var mark = {
16973             tag: "div",
16974             cls:"x-dlg-mask",
16975             style: "text-align:center",
16976             cn: [
16977                 {
16978                     tag: "div",
16979                     style: "background-color:white;width:50%;margin:250 auto",
16980                     cn: [
16981                         {
16982                             tag: "img",
16983                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16984                         },
16985                         {
16986                             tag: "span",
16987                             html: "Loading"
16988                         }
16989                         
16990                     ]
16991                 }
16992             ]
16993         };
16994         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16995         
16996         var size = this.el.select('.fc-content', true).first().getSize();
16997         this.maskEl.setSize(size.width, size.height);
16998         this.maskEl.enableDisplayMode("block");
16999         if(!this.loadMask){
17000             this.maskEl.hide();
17001         }
17002         
17003         this.store = Roo.factory(this.store, Roo.data);
17004         this.store.on('load', this.onLoad, this);
17005         this.store.on('beforeload', this.onBeforeLoad, this);
17006         
17007         this.resize();
17008         
17009         this.cells = this.el.select('.fc-day',true);
17010         //Roo.log(this.cells);
17011         this.textNodes = this.el.query('.fc-day-number');
17012         this.cells.addClassOnOver('fc-state-hover');
17013         
17014         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17015         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17016         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17017         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17018         
17019         this.on('monthchange', this.onMonthChange, this);
17020         
17021         this.update(new Date().clearTime());
17022     },
17023     
17024     resize : function() {
17025         var sz  = this.el.getSize();
17026         
17027         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17028         this.el.select('.fc-day-content div',true).setHeight(34);
17029     },
17030     
17031     
17032     // private
17033     showPrevMonth : function(e){
17034         this.update(this.activeDate.add("mo", -1));
17035     },
17036     showToday : function(e){
17037         this.update(new Date().clearTime());
17038     },
17039     // private
17040     showNextMonth : function(e){
17041         this.update(this.activeDate.add("mo", 1));
17042     },
17043
17044     // private
17045     showPrevYear : function(){
17046         this.update(this.activeDate.add("y", -1));
17047     },
17048
17049     // private
17050     showNextYear : function(){
17051         this.update(this.activeDate.add("y", 1));
17052     },
17053
17054     
17055    // private
17056     update : function(date)
17057     {
17058         var vd = this.activeDate;
17059         this.activeDate = date;
17060 //        if(vd && this.el){
17061 //            var t = date.getTime();
17062 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17063 //                Roo.log('using add remove');
17064 //                
17065 //                this.fireEvent('monthchange', this, date);
17066 //                
17067 //                this.cells.removeClass("fc-state-highlight");
17068 //                this.cells.each(function(c){
17069 //                   if(c.dateValue == t){
17070 //                       c.addClass("fc-state-highlight");
17071 //                       setTimeout(function(){
17072 //                            try{c.dom.firstChild.focus();}catch(e){}
17073 //                       }, 50);
17074 //                       return false;
17075 //                   }
17076 //                   return true;
17077 //                });
17078 //                return;
17079 //            }
17080 //        }
17081         
17082         var days = date.getDaysInMonth();
17083         
17084         var firstOfMonth = date.getFirstDateOfMonth();
17085         var startingPos = firstOfMonth.getDay()-this.startDay;
17086         
17087         if(startingPos < this.startDay){
17088             startingPos += 7;
17089         }
17090         
17091         var pm = date.add(Date.MONTH, -1);
17092         var prevStart = pm.getDaysInMonth()-startingPos;
17093 //        
17094         this.cells = this.el.select('.fc-day',true);
17095         this.textNodes = this.el.query('.fc-day-number');
17096         this.cells.addClassOnOver('fc-state-hover');
17097         
17098         var cells = this.cells.elements;
17099         var textEls = this.textNodes;
17100         
17101         Roo.each(cells, function(cell){
17102             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17103         });
17104         
17105         days += startingPos;
17106
17107         // convert everything to numbers so it's fast
17108         var day = 86400000;
17109         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17110         //Roo.log(d);
17111         //Roo.log(pm);
17112         //Roo.log(prevStart);
17113         
17114         var today = new Date().clearTime().getTime();
17115         var sel = date.clearTime().getTime();
17116         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17117         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17118         var ddMatch = this.disabledDatesRE;
17119         var ddText = this.disabledDatesText;
17120         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17121         var ddaysText = this.disabledDaysText;
17122         var format = this.format;
17123         
17124         var setCellClass = function(cal, cell){
17125             cell.row = 0;
17126             cell.events = [];
17127             cell.more = [];
17128             //Roo.log('set Cell Class');
17129             cell.title = "";
17130             var t = d.getTime();
17131             
17132             //Roo.log(d);
17133             
17134             cell.dateValue = t;
17135             if(t == today){
17136                 cell.className += " fc-today";
17137                 cell.className += " fc-state-highlight";
17138                 cell.title = cal.todayText;
17139             }
17140             if(t == sel){
17141                 // disable highlight in other month..
17142                 //cell.className += " fc-state-highlight";
17143                 
17144             }
17145             // disabling
17146             if(t < min) {
17147                 cell.className = " fc-state-disabled";
17148                 cell.title = cal.minText;
17149                 return;
17150             }
17151             if(t > max) {
17152                 cell.className = " fc-state-disabled";
17153                 cell.title = cal.maxText;
17154                 return;
17155             }
17156             if(ddays){
17157                 if(ddays.indexOf(d.getDay()) != -1){
17158                     cell.title = ddaysText;
17159                     cell.className = " fc-state-disabled";
17160                 }
17161             }
17162             if(ddMatch && format){
17163                 var fvalue = d.dateFormat(format);
17164                 if(ddMatch.test(fvalue)){
17165                     cell.title = ddText.replace("%0", fvalue);
17166                     cell.className = " fc-state-disabled";
17167                 }
17168             }
17169             
17170             if (!cell.initialClassName) {
17171                 cell.initialClassName = cell.dom.className;
17172             }
17173             
17174             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17175         };
17176
17177         var i = 0;
17178         
17179         for(; i < startingPos; i++) {
17180             textEls[i].innerHTML = (++prevStart);
17181             d.setDate(d.getDate()+1);
17182             
17183             cells[i].className = "fc-past fc-other-month";
17184             setCellClass(this, cells[i]);
17185         }
17186         
17187         var intDay = 0;
17188         
17189         for(; i < days; i++){
17190             intDay = i - startingPos + 1;
17191             textEls[i].innerHTML = (intDay);
17192             d.setDate(d.getDate()+1);
17193             
17194             cells[i].className = ''; // "x-date-active";
17195             setCellClass(this, cells[i]);
17196         }
17197         var extraDays = 0;
17198         
17199         for(; i < 42; i++) {
17200             textEls[i].innerHTML = (++extraDays);
17201             d.setDate(d.getDate()+1);
17202             
17203             cells[i].className = "fc-future fc-other-month";
17204             setCellClass(this, cells[i]);
17205         }
17206         
17207         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17208         
17209         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17210         
17211         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17212         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17213         
17214         if(totalRows != 6){
17215             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17216             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17217         }
17218         
17219         this.fireEvent('monthchange', this, date);
17220         
17221         
17222         /*
17223         if(!this.internalRender){
17224             var main = this.el.dom.firstChild;
17225             var w = main.offsetWidth;
17226             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17227             Roo.fly(main).setWidth(w);
17228             this.internalRender = true;
17229             // opera does not respect the auto grow header center column
17230             // then, after it gets a width opera refuses to recalculate
17231             // without a second pass
17232             if(Roo.isOpera && !this.secondPass){
17233                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17234                 this.secondPass = true;
17235                 this.update.defer(10, this, [date]);
17236             }
17237         }
17238         */
17239         
17240     },
17241     
17242     findCell : function(dt) {
17243         dt = dt.clearTime().getTime();
17244         var ret = false;
17245         this.cells.each(function(c){
17246             //Roo.log("check " +c.dateValue + '?=' + dt);
17247             if(c.dateValue == dt){
17248                 ret = c;
17249                 return false;
17250             }
17251             return true;
17252         });
17253         
17254         return ret;
17255     },
17256     
17257     findCells : function(ev) {
17258         var s = ev.start.clone().clearTime().getTime();
17259        // Roo.log(s);
17260         var e= ev.end.clone().clearTime().getTime();
17261        // Roo.log(e);
17262         var ret = [];
17263         this.cells.each(function(c){
17264              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17265             
17266             if(c.dateValue > e){
17267                 return ;
17268             }
17269             if(c.dateValue < s){
17270                 return ;
17271             }
17272             ret.push(c);
17273         });
17274         
17275         return ret;    
17276     },
17277     
17278 //    findBestRow: function(cells)
17279 //    {
17280 //        var ret = 0;
17281 //        
17282 //        for (var i =0 ; i < cells.length;i++) {
17283 //            ret  = Math.max(cells[i].rows || 0,ret);
17284 //        }
17285 //        return ret;
17286 //        
17287 //    },
17288     
17289     
17290     addItem : function(ev)
17291     {
17292         // look for vertical location slot in
17293         var cells = this.findCells(ev);
17294         
17295 //        ev.row = this.findBestRow(cells);
17296         
17297         // work out the location.
17298         
17299         var crow = false;
17300         var rows = [];
17301         for(var i =0; i < cells.length; i++) {
17302             
17303             cells[i].row = cells[0].row;
17304             
17305             if(i == 0){
17306                 cells[i].row = cells[i].row + 1;
17307             }
17308             
17309             if (!crow) {
17310                 crow = {
17311                     start : cells[i],
17312                     end :  cells[i]
17313                 };
17314                 continue;
17315             }
17316             if (crow.start.getY() == cells[i].getY()) {
17317                 // on same row.
17318                 crow.end = cells[i];
17319                 continue;
17320             }
17321             // different row.
17322             rows.push(crow);
17323             crow = {
17324                 start: cells[i],
17325                 end : cells[i]
17326             };
17327             
17328         }
17329         
17330         rows.push(crow);
17331         ev.els = [];
17332         ev.rows = rows;
17333         ev.cells = cells;
17334         
17335         cells[0].events.push(ev);
17336         
17337         this.calevents.push(ev);
17338     },
17339     
17340     clearEvents: function() {
17341         
17342         if(!this.calevents){
17343             return;
17344         }
17345         
17346         Roo.each(this.cells.elements, function(c){
17347             c.row = 0;
17348             c.events = [];
17349             c.more = [];
17350         });
17351         
17352         Roo.each(this.calevents, function(e) {
17353             Roo.each(e.els, function(el) {
17354                 el.un('mouseenter' ,this.onEventEnter, this);
17355                 el.un('mouseleave' ,this.onEventLeave, this);
17356                 el.remove();
17357             },this);
17358         },this);
17359         
17360         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17361             e.remove();
17362         });
17363         
17364     },
17365     
17366     renderEvents: function()
17367     {   
17368         var _this = this;
17369         
17370         this.cells.each(function(c) {
17371             
17372             if(c.row < 5){
17373                 return;
17374             }
17375             
17376             var ev = c.events;
17377             
17378             var r = 4;
17379             if(c.row != c.events.length){
17380                 r = 4 - (4 - (c.row - c.events.length));
17381             }
17382             
17383             c.events = ev.slice(0, r);
17384             c.more = ev.slice(r);
17385             
17386             if(c.more.length && c.more.length == 1){
17387                 c.events.push(c.more.pop());
17388             }
17389             
17390             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17391             
17392         });
17393             
17394         this.cells.each(function(c) {
17395             
17396             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17397             
17398             
17399             for (var e = 0; e < c.events.length; e++){
17400                 var ev = c.events[e];
17401                 var rows = ev.rows;
17402                 
17403                 for(var i = 0; i < rows.length; i++) {
17404                 
17405                     // how many rows should it span..
17406
17407                     var  cfg = {
17408                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17409                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17410
17411                         unselectable : "on",
17412                         cn : [
17413                             {
17414                                 cls: 'fc-event-inner',
17415                                 cn : [
17416     //                                {
17417     //                                  tag:'span',
17418     //                                  cls: 'fc-event-time',
17419     //                                  html : cells.length > 1 ? '' : ev.time
17420     //                                },
17421                                     {
17422                                       tag:'span',
17423                                       cls: 'fc-event-title',
17424                                       html : String.format('{0}', ev.title)
17425                                     }
17426
17427
17428                                 ]
17429                             },
17430                             {
17431                                 cls: 'ui-resizable-handle ui-resizable-e',
17432                                 html : '&nbsp;&nbsp;&nbsp'
17433                             }
17434
17435                         ]
17436                     };
17437
17438                     if (i == 0) {
17439                         cfg.cls += ' fc-event-start';
17440                     }
17441                     if ((i+1) == rows.length) {
17442                         cfg.cls += ' fc-event-end';
17443                     }
17444
17445                     var ctr = _this.el.select('.fc-event-container',true).first();
17446                     var cg = ctr.createChild(cfg);
17447
17448                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17449                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17450
17451                     var r = (c.more.length) ? 1 : 0;
17452                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17453                     cg.setWidth(ebox.right - sbox.x -2);
17454
17455                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17456                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17457                     cg.on('click', _this.onEventClick, _this, ev);
17458
17459                     ev.els.push(cg);
17460                     
17461                 }
17462                 
17463             }
17464             
17465             
17466             if(c.more.length){
17467                 var  cfg = {
17468                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17469                     style : 'position: absolute',
17470                     unselectable : "on",
17471                     cn : [
17472                         {
17473                             cls: 'fc-event-inner',
17474                             cn : [
17475                                 {
17476                                   tag:'span',
17477                                   cls: 'fc-event-title',
17478                                   html : 'More'
17479                                 }
17480
17481
17482                             ]
17483                         },
17484                         {
17485                             cls: 'ui-resizable-handle ui-resizable-e',
17486                             html : '&nbsp;&nbsp;&nbsp'
17487                         }
17488
17489                     ]
17490                 };
17491
17492                 var ctr = _this.el.select('.fc-event-container',true).first();
17493                 var cg = ctr.createChild(cfg);
17494
17495                 var sbox = c.select('.fc-day-content',true).first().getBox();
17496                 var ebox = c.select('.fc-day-content',true).first().getBox();
17497                 //Roo.log(cg);
17498                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17499                 cg.setWidth(ebox.right - sbox.x -2);
17500
17501                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17502                 
17503             }
17504             
17505         });
17506         
17507         
17508         
17509     },
17510     
17511     onEventEnter: function (e, el,event,d) {
17512         this.fireEvent('evententer', this, el, event);
17513     },
17514     
17515     onEventLeave: function (e, el,event,d) {
17516         this.fireEvent('eventleave', this, el, event);
17517     },
17518     
17519     onEventClick: function (e, el,event,d) {
17520         this.fireEvent('eventclick', this, el, event);
17521     },
17522     
17523     onMonthChange: function () {
17524         this.store.load();
17525     },
17526     
17527     onMoreEventClick: function(e, el, more)
17528     {
17529         var _this = this;
17530         
17531         this.calpopover.placement = 'right';
17532         this.calpopover.setTitle('More');
17533         
17534         this.calpopover.setContent('');
17535         
17536         var ctr = this.calpopover.el.select('.popover-content', true).first();
17537         
17538         Roo.each(more, function(m){
17539             var cfg = {
17540                 cls : 'fc-event-hori fc-event-draggable',
17541                 html : m.title
17542             };
17543             var cg = ctr.createChild(cfg);
17544             
17545             cg.on('click', _this.onEventClick, _this, m);
17546         });
17547         
17548         this.calpopover.show(el);
17549         
17550         
17551     },
17552     
17553     onLoad: function () 
17554     {   
17555         this.calevents = [];
17556         var cal = this;
17557         
17558         if(this.store.getCount() > 0){
17559             this.store.data.each(function(d){
17560                cal.addItem({
17561                     id : d.data.id,
17562                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17563                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17564                     time : d.data.start_time,
17565                     title : d.data.title,
17566                     description : d.data.description,
17567                     venue : d.data.venue
17568                 });
17569             });
17570         }
17571         
17572         this.renderEvents();
17573         
17574         if(this.calevents.length && this.loadMask){
17575             this.maskEl.hide();
17576         }
17577     },
17578     
17579     onBeforeLoad: function()
17580     {
17581         this.clearEvents();
17582         if(this.loadMask){
17583             this.maskEl.show();
17584         }
17585     }
17586 });
17587
17588  
17589  /*
17590  * - LGPL
17591  *
17592  * element
17593  * 
17594  */
17595
17596 /**
17597  * @class Roo.bootstrap.Popover
17598  * @extends Roo.bootstrap.Component
17599  * Bootstrap Popover class
17600  * @cfg {String} html contents of the popover   (or false to use children..)
17601  * @cfg {String} title of popover (or false to hide)
17602  * @cfg {String} placement how it is placed
17603  * @cfg {String} trigger click || hover (or false to trigger manually)
17604  * @cfg {String} over what (parent or false to trigger manually.)
17605  * @cfg {Number} delay - delay before showing
17606  
17607  * @constructor
17608  * Create a new Popover
17609  * @param {Object} config The config object
17610  */
17611
17612 Roo.bootstrap.Popover = function(config){
17613     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17614     
17615     this.addEvents({
17616         // raw events
17617          /**
17618          * @event show
17619          * After the popover show
17620          * 
17621          * @param {Roo.bootstrap.Popover} this
17622          */
17623         "show" : true,
17624         /**
17625          * @event hide
17626          * After the popover hide
17627          * 
17628          * @param {Roo.bootstrap.Popover} this
17629          */
17630         "hide" : true
17631     });
17632 };
17633
17634 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17635     
17636     title: 'Fill in a title',
17637     html: false,
17638     
17639     placement : 'right',
17640     trigger : 'hover', // hover
17641     
17642     delay : 0,
17643     
17644     over: 'parent',
17645     
17646     can_build_overlaid : false,
17647     
17648     getChildContainer : function()
17649     {
17650         return this.el.select('.popover-content',true).first();
17651     },
17652     
17653     getAutoCreate : function(){
17654          
17655         var cfg = {
17656            cls : 'popover roo-dynamic',
17657            style: 'display:block',
17658            cn : [
17659                 {
17660                     cls : 'arrow'
17661                 },
17662                 {
17663                     cls : 'popover-inner',
17664                     cn : [
17665                         {
17666                             tag: 'h3',
17667                             cls: 'popover-title popover-header',
17668                             html : this.title
17669                         },
17670                         {
17671                             cls : 'popover-content popover-body',
17672                             html : this.html
17673                         }
17674                     ]
17675                     
17676                 }
17677            ]
17678         };
17679         
17680         return cfg;
17681     },
17682     setTitle: function(str)
17683     {
17684         this.title = str;
17685         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17686     },
17687     setContent: function(str)
17688     {
17689         this.html = str;
17690         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17691     },
17692     // as it get's added to the bottom of the page.
17693     onRender : function(ct, position)
17694     {
17695         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17696         if(!this.el){
17697             var cfg = Roo.apply({},  this.getAutoCreate());
17698             cfg.id = Roo.id();
17699             
17700             if (this.cls) {
17701                 cfg.cls += ' ' + this.cls;
17702             }
17703             if (this.style) {
17704                 cfg.style = this.style;
17705             }
17706             //Roo.log("adding to ");
17707             this.el = Roo.get(document.body).createChild(cfg, position);
17708 //            Roo.log(this.el);
17709         }
17710         this.initEvents();
17711     },
17712     
17713     initEvents : function()
17714     {
17715         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17716         this.el.enableDisplayMode('block');
17717         this.el.hide();
17718         if (this.over === false) {
17719             return; 
17720         }
17721         if (this.triggers === false) {
17722             return;
17723         }
17724         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17725         var triggers = this.trigger ? this.trigger.split(' ') : [];
17726         Roo.each(triggers, function(trigger) {
17727         
17728             if (trigger == 'click') {
17729                 on_el.on('click', this.toggle, this);
17730             } else if (trigger != 'manual') {
17731                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17732                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17733       
17734                 on_el.on(eventIn  ,this.enter, this);
17735                 on_el.on(eventOut, this.leave, this);
17736             }
17737         }, this);
17738         
17739     },
17740     
17741     
17742     // private
17743     timeout : null,
17744     hoverState : null,
17745     
17746     toggle : function () {
17747         this.hoverState == 'in' ? this.leave() : this.enter();
17748     },
17749     
17750     enter : function () {
17751         
17752         clearTimeout(this.timeout);
17753     
17754         this.hoverState = 'in';
17755     
17756         if (!this.delay || !this.delay.show) {
17757             this.show();
17758             return;
17759         }
17760         var _t = this;
17761         this.timeout = setTimeout(function () {
17762             if (_t.hoverState == 'in') {
17763                 _t.show();
17764             }
17765         }, this.delay.show)
17766     },
17767     
17768     leave : function() {
17769         clearTimeout(this.timeout);
17770     
17771         this.hoverState = 'out';
17772     
17773         if (!this.delay || !this.delay.hide) {
17774             this.hide();
17775             return;
17776         }
17777         var _t = this;
17778         this.timeout = setTimeout(function () {
17779             if (_t.hoverState == 'out') {
17780                 _t.hide();
17781             }
17782         }, this.delay.hide)
17783     },
17784     
17785     show : function (on_el)
17786     {
17787         if (!on_el) {
17788             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17789         }
17790         
17791         // set content.
17792         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17793         if (this.html !== false) {
17794             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17795         }
17796         this.el.removeClass([
17797             'fade','top','bottom', 'left', 'right','in',
17798             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17799         ]);
17800         if (!this.title.length) {
17801             this.el.select('.popover-title',true).hide();
17802         }
17803         
17804         var placement = typeof this.placement == 'function' ?
17805             this.placement.call(this, this.el, on_el) :
17806             this.placement;
17807             
17808         var autoToken = /\s?auto?\s?/i;
17809         var autoPlace = autoToken.test(placement);
17810         if (autoPlace) {
17811             placement = placement.replace(autoToken, '') || 'top';
17812         }
17813         
17814         //this.el.detach()
17815         //this.el.setXY([0,0]);
17816         this.el.show();
17817         this.el.dom.style.display='block';
17818         this.el.addClass(placement);
17819         
17820         //this.el.appendTo(on_el);
17821         
17822         var p = this.getPosition();
17823         var box = this.el.getBox();
17824         
17825         if (autoPlace) {
17826             // fixme..
17827         }
17828         var align = Roo.bootstrap.Popover.alignment[placement];
17829         
17830 //        Roo.log(align);
17831         this.el.alignTo(on_el, align[0],align[1]);
17832         //var arrow = this.el.select('.arrow',true).first();
17833         //arrow.set(align[2], 
17834         
17835         this.el.addClass('in');
17836         
17837         
17838         if (this.el.hasClass('fade')) {
17839             // fade it?
17840         }
17841         
17842         this.hoverState = 'in';
17843         
17844         this.fireEvent('show', this);
17845         
17846     },
17847     hide : function()
17848     {
17849         this.el.setXY([0,0]);
17850         this.el.removeClass('in');
17851         this.el.hide();
17852         this.hoverState = null;
17853         
17854         this.fireEvent('hide', this);
17855     }
17856     
17857 });
17858
17859 Roo.bootstrap.Popover.alignment = {
17860     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17861     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17862     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17863     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17864 };
17865
17866  /*
17867  * - LGPL
17868  *
17869  * Progress
17870  * 
17871  */
17872
17873 /**
17874  * @class Roo.bootstrap.Progress
17875  * @extends Roo.bootstrap.Component
17876  * Bootstrap Progress class
17877  * @cfg {Boolean} striped striped of the progress bar
17878  * @cfg {Boolean} active animated of the progress bar
17879  * 
17880  * 
17881  * @constructor
17882  * Create a new Progress
17883  * @param {Object} config The config object
17884  */
17885
17886 Roo.bootstrap.Progress = function(config){
17887     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17888 };
17889
17890 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17891     
17892     striped : false,
17893     active: false,
17894     
17895     getAutoCreate : function(){
17896         var cfg = {
17897             tag: 'div',
17898             cls: 'progress'
17899         };
17900         
17901         
17902         if(this.striped){
17903             cfg.cls += ' progress-striped';
17904         }
17905       
17906         if(this.active){
17907             cfg.cls += ' active';
17908         }
17909         
17910         
17911         return cfg;
17912     }
17913    
17914 });
17915
17916  
17917
17918  /*
17919  * - LGPL
17920  *
17921  * ProgressBar
17922  * 
17923  */
17924
17925 /**
17926  * @class Roo.bootstrap.ProgressBar
17927  * @extends Roo.bootstrap.Component
17928  * Bootstrap ProgressBar class
17929  * @cfg {Number} aria_valuenow aria-value now
17930  * @cfg {Number} aria_valuemin aria-value min
17931  * @cfg {Number} aria_valuemax aria-value max
17932  * @cfg {String} label label for the progress bar
17933  * @cfg {String} panel (success | info | warning | danger )
17934  * @cfg {String} role role of the progress bar
17935  * @cfg {String} sr_only text
17936  * 
17937  * 
17938  * @constructor
17939  * Create a new ProgressBar
17940  * @param {Object} config The config object
17941  */
17942
17943 Roo.bootstrap.ProgressBar = function(config){
17944     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17945 };
17946
17947 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17948     
17949     aria_valuenow : 0,
17950     aria_valuemin : 0,
17951     aria_valuemax : 100,
17952     label : false,
17953     panel : false,
17954     role : false,
17955     sr_only: false,
17956     
17957     getAutoCreate : function()
17958     {
17959         
17960         var cfg = {
17961             tag: 'div',
17962             cls: 'progress-bar',
17963             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17964         };
17965         
17966         if(this.sr_only){
17967             cfg.cn = {
17968                 tag: 'span',
17969                 cls: 'sr-only',
17970                 html: this.sr_only
17971             }
17972         }
17973         
17974         if(this.role){
17975             cfg.role = this.role;
17976         }
17977         
17978         if(this.aria_valuenow){
17979             cfg['aria-valuenow'] = this.aria_valuenow;
17980         }
17981         
17982         if(this.aria_valuemin){
17983             cfg['aria-valuemin'] = this.aria_valuemin;
17984         }
17985         
17986         if(this.aria_valuemax){
17987             cfg['aria-valuemax'] = this.aria_valuemax;
17988         }
17989         
17990         if(this.label && !this.sr_only){
17991             cfg.html = this.label;
17992         }
17993         
17994         if(this.panel){
17995             cfg.cls += ' progress-bar-' + this.panel;
17996         }
17997         
17998         return cfg;
17999     },
18000     
18001     update : function(aria_valuenow)
18002     {
18003         this.aria_valuenow = aria_valuenow;
18004         
18005         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18006     }
18007    
18008 });
18009
18010  
18011
18012  /*
18013  * - LGPL
18014  *
18015  * column
18016  * 
18017  */
18018
18019 /**
18020  * @class Roo.bootstrap.TabGroup
18021  * @extends Roo.bootstrap.Column
18022  * Bootstrap Column class
18023  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18024  * @cfg {Boolean} carousel true to make the group behave like a carousel
18025  * @cfg {Boolean} bullets show bullets for the panels
18026  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18027  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18028  * @cfg {Boolean} showarrow (true|false) show arrow default true
18029  * 
18030  * @constructor
18031  * Create a new TabGroup
18032  * @param {Object} config The config object
18033  */
18034
18035 Roo.bootstrap.TabGroup = function(config){
18036     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18037     if (!this.navId) {
18038         this.navId = Roo.id();
18039     }
18040     this.tabs = [];
18041     Roo.bootstrap.TabGroup.register(this);
18042     
18043 };
18044
18045 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18046     
18047     carousel : false,
18048     transition : false,
18049     bullets : 0,
18050     timer : 0,
18051     autoslide : false,
18052     slideFn : false,
18053     slideOnTouch : false,
18054     showarrow : true,
18055     
18056     getAutoCreate : function()
18057     {
18058         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18059         
18060         cfg.cls += ' tab-content';
18061         
18062         if (this.carousel) {
18063             cfg.cls += ' carousel slide';
18064             
18065             cfg.cn = [{
18066                cls : 'carousel-inner',
18067                cn : []
18068             }];
18069         
18070             if(this.bullets  && !Roo.isTouch){
18071                 
18072                 var bullets = {
18073                     cls : 'carousel-bullets',
18074                     cn : []
18075                 };
18076                
18077                 if(this.bullets_cls){
18078                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18079                 }
18080                 
18081                 bullets.cn.push({
18082                     cls : 'clear'
18083                 });
18084                 
18085                 cfg.cn[0].cn.push(bullets);
18086             }
18087             
18088             if(this.showarrow){
18089                 cfg.cn[0].cn.push({
18090                     tag : 'div',
18091                     class : 'carousel-arrow',
18092                     cn : [
18093                         {
18094                             tag : 'div',
18095                             class : 'carousel-prev',
18096                             cn : [
18097                                 {
18098                                     tag : 'i',
18099                                     class : 'fa fa-chevron-left'
18100                                 }
18101                             ]
18102                         },
18103                         {
18104                             tag : 'div',
18105                             class : 'carousel-next',
18106                             cn : [
18107                                 {
18108                                     tag : 'i',
18109                                     class : 'fa fa-chevron-right'
18110                                 }
18111                             ]
18112                         }
18113                     ]
18114                 });
18115             }
18116             
18117         }
18118         
18119         return cfg;
18120     },
18121     
18122     initEvents:  function()
18123     {
18124 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18125 //            this.el.on("touchstart", this.onTouchStart, this);
18126 //        }
18127         
18128         if(this.autoslide){
18129             var _this = this;
18130             
18131             this.slideFn = window.setInterval(function() {
18132                 _this.showPanelNext();
18133             }, this.timer);
18134         }
18135         
18136         if(this.showarrow){
18137             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18138             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18139         }
18140         
18141         
18142     },
18143     
18144 //    onTouchStart : function(e, el, o)
18145 //    {
18146 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18147 //            return;
18148 //        }
18149 //        
18150 //        this.showPanelNext();
18151 //    },
18152     
18153     
18154     getChildContainer : function()
18155     {
18156         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18157     },
18158     
18159     /**
18160     * register a Navigation item
18161     * @param {Roo.bootstrap.NavItem} the navitem to add
18162     */
18163     register : function(item)
18164     {
18165         this.tabs.push( item);
18166         item.navId = this.navId; // not really needed..
18167         this.addBullet();
18168     
18169     },
18170     
18171     getActivePanel : function()
18172     {
18173         var r = false;
18174         Roo.each(this.tabs, function(t) {
18175             if (t.active) {
18176                 r = t;
18177                 return false;
18178             }
18179             return null;
18180         });
18181         return r;
18182         
18183     },
18184     getPanelByName : function(n)
18185     {
18186         var r = false;
18187         Roo.each(this.tabs, function(t) {
18188             if (t.tabId == n) {
18189                 r = t;
18190                 return false;
18191             }
18192             return null;
18193         });
18194         return r;
18195     },
18196     indexOfPanel : function(p)
18197     {
18198         var r = false;
18199         Roo.each(this.tabs, function(t,i) {
18200             if (t.tabId == p.tabId) {
18201                 r = i;
18202                 return false;
18203             }
18204             return null;
18205         });
18206         return r;
18207     },
18208     /**
18209      * show a specific panel
18210      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18211      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18212      */
18213     showPanel : function (pan)
18214     {
18215         if(this.transition || typeof(pan) == 'undefined'){
18216             Roo.log("waiting for the transitionend");
18217             return;
18218         }
18219         
18220         if (typeof(pan) == 'number') {
18221             pan = this.tabs[pan];
18222         }
18223         
18224         if (typeof(pan) == 'string') {
18225             pan = this.getPanelByName(pan);
18226         }
18227         
18228         var cur = this.getActivePanel();
18229         
18230         if(!pan || !cur){
18231             Roo.log('pan or acitve pan is undefined');
18232             return false;
18233         }
18234         
18235         if (pan.tabId == this.getActivePanel().tabId) {
18236             return true;
18237         }
18238         
18239         if (false === cur.fireEvent('beforedeactivate')) {
18240             return false;
18241         }
18242         
18243         if(this.bullets > 0 && !Roo.isTouch){
18244             this.setActiveBullet(this.indexOfPanel(pan));
18245         }
18246         
18247         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18248             
18249             this.transition = true;
18250             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18251             var lr = dir == 'next' ? 'left' : 'right';
18252             pan.el.addClass(dir); // or prev
18253             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18254             cur.el.addClass(lr); // or right
18255             pan.el.addClass(lr);
18256             
18257             var _this = this;
18258             cur.el.on('transitionend', function() {
18259                 Roo.log("trans end?");
18260                 
18261                 pan.el.removeClass([lr,dir]);
18262                 pan.setActive(true);
18263                 
18264                 cur.el.removeClass([lr]);
18265                 cur.setActive(false);
18266                 
18267                 _this.transition = false;
18268                 
18269             }, this, { single:  true } );
18270             
18271             return true;
18272         }
18273         
18274         cur.setActive(false);
18275         pan.setActive(true);
18276         
18277         return true;
18278         
18279     },
18280     showPanelNext : function()
18281     {
18282         var i = this.indexOfPanel(this.getActivePanel());
18283         
18284         if (i >= this.tabs.length - 1 && !this.autoslide) {
18285             return;
18286         }
18287         
18288         if (i >= this.tabs.length - 1 && this.autoslide) {
18289             i = -1;
18290         }
18291         
18292         this.showPanel(this.tabs[i+1]);
18293     },
18294     
18295     showPanelPrev : function()
18296     {
18297         var i = this.indexOfPanel(this.getActivePanel());
18298         
18299         if (i  < 1 && !this.autoslide) {
18300             return;
18301         }
18302         
18303         if (i < 1 && this.autoslide) {
18304             i = this.tabs.length;
18305         }
18306         
18307         this.showPanel(this.tabs[i-1]);
18308     },
18309     
18310     
18311     addBullet: function()
18312     {
18313         if(!this.bullets || Roo.isTouch){
18314             return;
18315         }
18316         var ctr = this.el.select('.carousel-bullets',true).first();
18317         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18318         var bullet = ctr.createChild({
18319             cls : 'bullet bullet-' + i
18320         },ctr.dom.lastChild);
18321         
18322         
18323         var _this = this;
18324         
18325         bullet.on('click', (function(e, el, o, ii, t){
18326
18327             e.preventDefault();
18328
18329             this.showPanel(ii);
18330
18331             if(this.autoslide && this.slideFn){
18332                 clearInterval(this.slideFn);
18333                 this.slideFn = window.setInterval(function() {
18334                     _this.showPanelNext();
18335                 }, this.timer);
18336             }
18337
18338         }).createDelegate(this, [i, bullet], true));
18339                 
18340         
18341     },
18342      
18343     setActiveBullet : function(i)
18344     {
18345         if(Roo.isTouch){
18346             return;
18347         }
18348         
18349         Roo.each(this.el.select('.bullet', true).elements, function(el){
18350             el.removeClass('selected');
18351         });
18352
18353         var bullet = this.el.select('.bullet-' + i, true).first();
18354         
18355         if(!bullet){
18356             return;
18357         }
18358         
18359         bullet.addClass('selected');
18360     }
18361     
18362     
18363   
18364 });
18365
18366  
18367
18368  
18369  
18370 Roo.apply(Roo.bootstrap.TabGroup, {
18371     
18372     groups: {},
18373      /**
18374     * register a Navigation Group
18375     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18376     */
18377     register : function(navgrp)
18378     {
18379         this.groups[navgrp.navId] = navgrp;
18380         
18381     },
18382     /**
18383     * fetch a Navigation Group based on the navigation ID
18384     * if one does not exist , it will get created.
18385     * @param {string} the navgroup to add
18386     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18387     */
18388     get: function(navId) {
18389         if (typeof(this.groups[navId]) == 'undefined') {
18390             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18391         }
18392         return this.groups[navId] ;
18393     }
18394     
18395     
18396     
18397 });
18398
18399  /*
18400  * - LGPL
18401  *
18402  * TabPanel
18403  * 
18404  */
18405
18406 /**
18407  * @class Roo.bootstrap.TabPanel
18408  * @extends Roo.bootstrap.Component
18409  * Bootstrap TabPanel class
18410  * @cfg {Boolean} active panel active
18411  * @cfg {String} html panel content
18412  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18413  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18414  * @cfg {String} href click to link..
18415  * 
18416  * 
18417  * @constructor
18418  * Create a new TabPanel
18419  * @param {Object} config The config object
18420  */
18421
18422 Roo.bootstrap.TabPanel = function(config){
18423     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18424     this.addEvents({
18425         /**
18426              * @event changed
18427              * Fires when the active status changes
18428              * @param {Roo.bootstrap.TabPanel} this
18429              * @param {Boolean} state the new state
18430             
18431          */
18432         'changed': true,
18433         /**
18434              * @event beforedeactivate
18435              * Fires before a tab is de-activated - can be used to do validation on a form.
18436              * @param {Roo.bootstrap.TabPanel} this
18437              * @return {Boolean} false if there is an error
18438             
18439          */
18440         'beforedeactivate': true
18441      });
18442     
18443     this.tabId = this.tabId || Roo.id();
18444   
18445 };
18446
18447 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18448     
18449     active: false,
18450     html: false,
18451     tabId: false,
18452     navId : false,
18453     href : '',
18454     
18455     getAutoCreate : function(){
18456         var cfg = {
18457             tag: 'div',
18458             // item is needed for carousel - not sure if it has any effect otherwise
18459             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18460             html: this.html || ''
18461         };
18462         
18463         if(this.active){
18464             cfg.cls += ' active';
18465         }
18466         
18467         if(this.tabId){
18468             cfg.tabId = this.tabId;
18469         }
18470         
18471         
18472         return cfg;
18473     },
18474     
18475     initEvents:  function()
18476     {
18477         var p = this.parent();
18478         
18479         this.navId = this.navId || p.navId;
18480         
18481         if (typeof(this.navId) != 'undefined') {
18482             // not really needed.. but just in case.. parent should be a NavGroup.
18483             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18484             
18485             tg.register(this);
18486             
18487             var i = tg.tabs.length - 1;
18488             
18489             if(this.active && tg.bullets > 0 && i < tg.bullets){
18490                 tg.setActiveBullet(i);
18491             }
18492         }
18493         
18494         this.el.on('click', this.onClick, this);
18495         
18496         if(Roo.isTouch){
18497             this.el.on("touchstart", this.onTouchStart, this);
18498             this.el.on("touchmove", this.onTouchMove, this);
18499             this.el.on("touchend", this.onTouchEnd, this);
18500         }
18501         
18502     },
18503     
18504     onRender : function(ct, position)
18505     {
18506         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18507     },
18508     
18509     setActive : function(state)
18510     {
18511         Roo.log("panel - set active " + this.tabId + "=" + state);
18512         
18513         this.active = state;
18514         if (!state) {
18515             this.el.removeClass('active');
18516             
18517         } else  if (!this.el.hasClass('active')) {
18518             this.el.addClass('active');
18519         }
18520         
18521         this.fireEvent('changed', this, state);
18522     },
18523     
18524     onClick : function(e)
18525     {
18526         e.preventDefault();
18527         
18528         if(!this.href.length){
18529             return;
18530         }
18531         
18532         window.location.href = this.href;
18533     },
18534     
18535     startX : 0,
18536     startY : 0,
18537     endX : 0,
18538     endY : 0,
18539     swiping : false,
18540     
18541     onTouchStart : function(e)
18542     {
18543         this.swiping = false;
18544         
18545         this.startX = e.browserEvent.touches[0].clientX;
18546         this.startY = e.browserEvent.touches[0].clientY;
18547     },
18548     
18549     onTouchMove : function(e)
18550     {
18551         this.swiping = true;
18552         
18553         this.endX = e.browserEvent.touches[0].clientX;
18554         this.endY = e.browserEvent.touches[0].clientY;
18555     },
18556     
18557     onTouchEnd : function(e)
18558     {
18559         if(!this.swiping){
18560             this.onClick(e);
18561             return;
18562         }
18563         
18564         var tabGroup = this.parent();
18565         
18566         if(this.endX > this.startX){ // swiping right
18567             tabGroup.showPanelPrev();
18568             return;
18569         }
18570         
18571         if(this.startX > this.endX){ // swiping left
18572             tabGroup.showPanelNext();
18573             return;
18574         }
18575     }
18576     
18577     
18578 });
18579  
18580
18581  
18582
18583  /*
18584  * - LGPL
18585  *
18586  * DateField
18587  * 
18588  */
18589
18590 /**
18591  * @class Roo.bootstrap.DateField
18592  * @extends Roo.bootstrap.Input
18593  * Bootstrap DateField class
18594  * @cfg {Number} weekStart default 0
18595  * @cfg {String} viewMode default empty, (months|years)
18596  * @cfg {String} minViewMode default empty, (months|years)
18597  * @cfg {Number} startDate default -Infinity
18598  * @cfg {Number} endDate default Infinity
18599  * @cfg {Boolean} todayHighlight default false
18600  * @cfg {Boolean} todayBtn default false
18601  * @cfg {Boolean} calendarWeeks default false
18602  * @cfg {Object} daysOfWeekDisabled default empty
18603  * @cfg {Boolean} singleMode default false (true | false)
18604  * 
18605  * @cfg {Boolean} keyboardNavigation default true
18606  * @cfg {String} language default en
18607  * 
18608  * @constructor
18609  * Create a new DateField
18610  * @param {Object} config The config object
18611  */
18612
18613 Roo.bootstrap.DateField = function(config){
18614     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18615      this.addEvents({
18616             /**
18617              * @event show
18618              * Fires when this field show.
18619              * @param {Roo.bootstrap.DateField} this
18620              * @param {Mixed} date The date value
18621              */
18622             show : true,
18623             /**
18624              * @event show
18625              * Fires when this field hide.
18626              * @param {Roo.bootstrap.DateField} this
18627              * @param {Mixed} date The date value
18628              */
18629             hide : true,
18630             /**
18631              * @event select
18632              * Fires when select a date.
18633              * @param {Roo.bootstrap.DateField} this
18634              * @param {Mixed} date The date value
18635              */
18636             select : true,
18637             /**
18638              * @event beforeselect
18639              * Fires when before select a date.
18640              * @param {Roo.bootstrap.DateField} this
18641              * @param {Mixed} date The date value
18642              */
18643             beforeselect : true
18644         });
18645 };
18646
18647 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18648     
18649     /**
18650      * @cfg {String} format
18651      * The default date format string which can be overriden for localization support.  The format must be
18652      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18653      */
18654     format : "m/d/y",
18655     /**
18656      * @cfg {String} altFormats
18657      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18658      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18659      */
18660     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18661     
18662     weekStart : 0,
18663     
18664     viewMode : '',
18665     
18666     minViewMode : '',
18667     
18668     todayHighlight : false,
18669     
18670     todayBtn: false,
18671     
18672     language: 'en',
18673     
18674     keyboardNavigation: true,
18675     
18676     calendarWeeks: false,
18677     
18678     startDate: -Infinity,
18679     
18680     endDate: Infinity,
18681     
18682     daysOfWeekDisabled: [],
18683     
18684     _events: [],
18685     
18686     singleMode : false,
18687     
18688     UTCDate: function()
18689     {
18690         return new Date(Date.UTC.apply(Date, arguments));
18691     },
18692     
18693     UTCToday: function()
18694     {
18695         var today = new Date();
18696         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18697     },
18698     
18699     getDate: function() {
18700             var d = this.getUTCDate();
18701             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18702     },
18703     
18704     getUTCDate: function() {
18705             return this.date;
18706     },
18707     
18708     setDate: function(d) {
18709             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18710     },
18711     
18712     setUTCDate: function(d) {
18713             this.date = d;
18714             this.setValue(this.formatDate(this.date));
18715     },
18716         
18717     onRender: function(ct, position)
18718     {
18719         
18720         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18721         
18722         this.language = this.language || 'en';
18723         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18724         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18725         
18726         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18727         this.format = this.format || 'm/d/y';
18728         this.isInline = false;
18729         this.isInput = true;
18730         this.component = this.el.select('.add-on', true).first() || false;
18731         this.component = (this.component && this.component.length === 0) ? false : this.component;
18732         this.hasInput = this.component && this.inputEl().length;
18733         
18734         if (typeof(this.minViewMode === 'string')) {
18735             switch (this.minViewMode) {
18736                 case 'months':
18737                     this.minViewMode = 1;
18738                     break;
18739                 case 'years':
18740                     this.minViewMode = 2;
18741                     break;
18742                 default:
18743                     this.minViewMode = 0;
18744                     break;
18745             }
18746         }
18747         
18748         if (typeof(this.viewMode === 'string')) {
18749             switch (this.viewMode) {
18750                 case 'months':
18751                     this.viewMode = 1;
18752                     break;
18753                 case 'years':
18754                     this.viewMode = 2;
18755                     break;
18756                 default:
18757                     this.viewMode = 0;
18758                     break;
18759             }
18760         }
18761                 
18762         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18763         
18764 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18765         
18766         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18767         
18768         this.picker().on('mousedown', this.onMousedown, this);
18769         this.picker().on('click', this.onClick, this);
18770         
18771         this.picker().addClass('datepicker-dropdown');
18772         
18773         this.startViewMode = this.viewMode;
18774         
18775         if(this.singleMode){
18776             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18777                 v.setVisibilityMode(Roo.Element.DISPLAY);
18778                 v.hide();
18779             });
18780             
18781             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18782                 v.setStyle('width', '189px');
18783             });
18784         }
18785         
18786         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18787             if(!this.calendarWeeks){
18788                 v.remove();
18789                 return;
18790             }
18791             
18792             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18793             v.attr('colspan', function(i, val){
18794                 return parseInt(val) + 1;
18795             });
18796         });
18797                         
18798         
18799         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18800         
18801         this.setStartDate(this.startDate);
18802         this.setEndDate(this.endDate);
18803         
18804         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18805         
18806         this.fillDow();
18807         this.fillMonths();
18808         this.update();
18809         this.showMode();
18810         
18811         if(this.isInline) {
18812             this.showPopup();
18813         }
18814     },
18815     
18816     picker : function()
18817     {
18818         return this.pickerEl;
18819 //        return this.el.select('.datepicker', true).first();
18820     },
18821     
18822     fillDow: function()
18823     {
18824         var dowCnt = this.weekStart;
18825         
18826         var dow = {
18827             tag: 'tr',
18828             cn: [
18829                 
18830             ]
18831         };
18832         
18833         if(this.calendarWeeks){
18834             dow.cn.push({
18835                 tag: 'th',
18836                 cls: 'cw',
18837                 html: '&nbsp;'
18838             })
18839         }
18840         
18841         while (dowCnt < this.weekStart + 7) {
18842             dow.cn.push({
18843                 tag: 'th',
18844                 cls: 'dow',
18845                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18846             });
18847         }
18848         
18849         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18850     },
18851     
18852     fillMonths: function()
18853     {    
18854         var i = 0;
18855         var months = this.picker().select('>.datepicker-months td', true).first();
18856         
18857         months.dom.innerHTML = '';
18858         
18859         while (i < 12) {
18860             var month = {
18861                 tag: 'span',
18862                 cls: 'month',
18863                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18864             };
18865             
18866             months.createChild(month);
18867         }
18868         
18869     },
18870     
18871     update: function()
18872     {
18873         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;
18874         
18875         if (this.date < this.startDate) {
18876             this.viewDate = new Date(this.startDate);
18877         } else if (this.date > this.endDate) {
18878             this.viewDate = new Date(this.endDate);
18879         } else {
18880             this.viewDate = new Date(this.date);
18881         }
18882         
18883         this.fill();
18884     },
18885     
18886     fill: function() 
18887     {
18888         var d = new Date(this.viewDate),
18889                 year = d.getUTCFullYear(),
18890                 month = d.getUTCMonth(),
18891                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18892                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18893                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18894                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18895                 currentDate = this.date && this.date.valueOf(),
18896                 today = this.UTCToday();
18897         
18898         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18899         
18900 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18901         
18902 //        this.picker.select('>tfoot th.today').
18903 //                                              .text(dates[this.language].today)
18904 //                                              .toggle(this.todayBtn !== false);
18905     
18906         this.updateNavArrows();
18907         this.fillMonths();
18908                                                 
18909         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18910         
18911         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18912          
18913         prevMonth.setUTCDate(day);
18914         
18915         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18916         
18917         var nextMonth = new Date(prevMonth);
18918         
18919         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18920         
18921         nextMonth = nextMonth.valueOf();
18922         
18923         var fillMonths = false;
18924         
18925         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18926         
18927         while(prevMonth.valueOf() <= nextMonth) {
18928             var clsName = '';
18929             
18930             if (prevMonth.getUTCDay() === this.weekStart) {
18931                 if(fillMonths){
18932                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18933                 }
18934                     
18935                 fillMonths = {
18936                     tag: 'tr',
18937                     cn: []
18938                 };
18939                 
18940                 if(this.calendarWeeks){
18941                     // ISO 8601: First week contains first thursday.
18942                     // ISO also states week starts on Monday, but we can be more abstract here.
18943                     var
18944                     // Start of current week: based on weekstart/current date
18945                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18946                     // Thursday of this week
18947                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18948                     // First Thursday of year, year from thursday
18949                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18950                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18951                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18952                     
18953                     fillMonths.cn.push({
18954                         tag: 'td',
18955                         cls: 'cw',
18956                         html: calWeek
18957                     });
18958                 }
18959             }
18960             
18961             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18962                 clsName += ' old';
18963             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18964                 clsName += ' new';
18965             }
18966             if (this.todayHighlight &&
18967                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18968                 prevMonth.getUTCMonth() == today.getMonth() &&
18969                 prevMonth.getUTCDate() == today.getDate()) {
18970                 clsName += ' today';
18971             }
18972             
18973             if (currentDate && prevMonth.valueOf() === currentDate) {
18974                 clsName += ' active';
18975             }
18976             
18977             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18978                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18979                     clsName += ' disabled';
18980             }
18981             
18982             fillMonths.cn.push({
18983                 tag: 'td',
18984                 cls: 'day ' + clsName,
18985                 html: prevMonth.getDate()
18986             });
18987             
18988             prevMonth.setDate(prevMonth.getDate()+1);
18989         }
18990           
18991         var currentYear = this.date && this.date.getUTCFullYear();
18992         var currentMonth = this.date && this.date.getUTCMonth();
18993         
18994         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18995         
18996         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18997             v.removeClass('active');
18998             
18999             if(currentYear === year && k === currentMonth){
19000                 v.addClass('active');
19001             }
19002             
19003             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19004                 v.addClass('disabled');
19005             }
19006             
19007         });
19008         
19009         
19010         year = parseInt(year/10, 10) * 10;
19011         
19012         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19013         
19014         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19015         
19016         year -= 1;
19017         for (var i = -1; i < 11; i++) {
19018             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19019                 tag: 'span',
19020                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19021                 html: year
19022             });
19023             
19024             year += 1;
19025         }
19026     },
19027     
19028     showMode: function(dir) 
19029     {
19030         if (dir) {
19031             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19032         }
19033         
19034         Roo.each(this.picker().select('>div',true).elements, function(v){
19035             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19036             v.hide();
19037         });
19038         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19039     },
19040     
19041     place: function()
19042     {
19043         if(this.isInline) {
19044             return;
19045         }
19046         
19047         this.picker().removeClass(['bottom', 'top']);
19048         
19049         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19050             /*
19051              * place to the top of element!
19052              *
19053              */
19054             
19055             this.picker().addClass('top');
19056             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19057             
19058             return;
19059         }
19060         
19061         this.picker().addClass('bottom');
19062         
19063         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19064     },
19065     
19066     parseDate : function(value)
19067     {
19068         if(!value || value instanceof Date){
19069             return value;
19070         }
19071         var v = Date.parseDate(value, this.format);
19072         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19073             v = Date.parseDate(value, 'Y-m-d');
19074         }
19075         if(!v && this.altFormats){
19076             if(!this.altFormatsArray){
19077                 this.altFormatsArray = this.altFormats.split("|");
19078             }
19079             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19080                 v = Date.parseDate(value, this.altFormatsArray[i]);
19081             }
19082         }
19083         return v;
19084     },
19085     
19086     formatDate : function(date, fmt)
19087     {   
19088         return (!date || !(date instanceof Date)) ?
19089         date : date.dateFormat(fmt || this.format);
19090     },
19091     
19092     onFocus : function()
19093     {
19094         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19095         this.showPopup();
19096     },
19097     
19098     onBlur : function()
19099     {
19100         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19101         
19102         var d = this.inputEl().getValue();
19103         
19104         this.setValue(d);
19105                 
19106         this.hidePopup();
19107     },
19108     
19109     showPopup : function()
19110     {
19111         this.picker().show();
19112         this.update();
19113         this.place();
19114         
19115         this.fireEvent('showpopup', this, this.date);
19116     },
19117     
19118     hidePopup : function()
19119     {
19120         if(this.isInline) {
19121             return;
19122         }
19123         this.picker().hide();
19124         this.viewMode = this.startViewMode;
19125         this.showMode();
19126         
19127         this.fireEvent('hidepopup', this, this.date);
19128         
19129     },
19130     
19131     onMousedown: function(e)
19132     {
19133         e.stopPropagation();
19134         e.preventDefault();
19135     },
19136     
19137     keyup: function(e)
19138     {
19139         Roo.bootstrap.DateField.superclass.keyup.call(this);
19140         this.update();
19141     },
19142
19143     setValue: function(v)
19144     {
19145         if(this.fireEvent('beforeselect', this, v) !== false){
19146             var d = new Date(this.parseDate(v) ).clearTime();
19147         
19148             if(isNaN(d.getTime())){
19149                 this.date = this.viewDate = '';
19150                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19151                 return;
19152             }
19153
19154             v = this.formatDate(d);
19155
19156             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19157
19158             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19159
19160             this.update();
19161
19162             this.fireEvent('select', this, this.date);
19163         }
19164     },
19165     
19166     getValue: function()
19167     {
19168         return this.formatDate(this.date);
19169     },
19170     
19171     fireKey: function(e)
19172     {
19173         if (!this.picker().isVisible()){
19174             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19175                 this.showPopup();
19176             }
19177             return;
19178         }
19179         
19180         var dateChanged = false,
19181         dir, day, month,
19182         newDate, newViewDate;
19183         
19184         switch(e.keyCode){
19185             case 27: // escape
19186                 this.hidePopup();
19187                 e.preventDefault();
19188                 break;
19189             case 37: // left
19190             case 39: // right
19191                 if (!this.keyboardNavigation) {
19192                     break;
19193                 }
19194                 dir = e.keyCode == 37 ? -1 : 1;
19195                 
19196                 if (e.ctrlKey){
19197                     newDate = this.moveYear(this.date, dir);
19198                     newViewDate = this.moveYear(this.viewDate, dir);
19199                 } else if (e.shiftKey){
19200                     newDate = this.moveMonth(this.date, dir);
19201                     newViewDate = this.moveMonth(this.viewDate, dir);
19202                 } else {
19203                     newDate = new Date(this.date);
19204                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19205                     newViewDate = new Date(this.viewDate);
19206                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19207                 }
19208                 if (this.dateWithinRange(newDate)){
19209                     this.date = newDate;
19210                     this.viewDate = newViewDate;
19211                     this.setValue(this.formatDate(this.date));
19212 //                    this.update();
19213                     e.preventDefault();
19214                     dateChanged = true;
19215                 }
19216                 break;
19217             case 38: // up
19218             case 40: // down
19219                 if (!this.keyboardNavigation) {
19220                     break;
19221                 }
19222                 dir = e.keyCode == 38 ? -1 : 1;
19223                 if (e.ctrlKey){
19224                     newDate = this.moveYear(this.date, dir);
19225                     newViewDate = this.moveYear(this.viewDate, dir);
19226                 } else if (e.shiftKey){
19227                     newDate = this.moveMonth(this.date, dir);
19228                     newViewDate = this.moveMonth(this.viewDate, dir);
19229                 } else {
19230                     newDate = new Date(this.date);
19231                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19232                     newViewDate = new Date(this.viewDate);
19233                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19234                 }
19235                 if (this.dateWithinRange(newDate)){
19236                     this.date = newDate;
19237                     this.viewDate = newViewDate;
19238                     this.setValue(this.formatDate(this.date));
19239 //                    this.update();
19240                     e.preventDefault();
19241                     dateChanged = true;
19242                 }
19243                 break;
19244             case 13: // enter
19245                 this.setValue(this.formatDate(this.date));
19246                 this.hidePopup();
19247                 e.preventDefault();
19248                 break;
19249             case 9: // tab
19250                 this.setValue(this.formatDate(this.date));
19251                 this.hidePopup();
19252                 break;
19253             case 16: // shift
19254             case 17: // ctrl
19255             case 18: // alt
19256                 break;
19257             default :
19258                 this.hidePopup();
19259                 
19260         }
19261     },
19262     
19263     
19264     onClick: function(e) 
19265     {
19266         e.stopPropagation();
19267         e.preventDefault();
19268         
19269         var target = e.getTarget();
19270         
19271         if(target.nodeName.toLowerCase() === 'i'){
19272             target = Roo.get(target).dom.parentNode;
19273         }
19274         
19275         var nodeName = target.nodeName;
19276         var className = target.className;
19277         var html = target.innerHTML;
19278         //Roo.log(nodeName);
19279         
19280         switch(nodeName.toLowerCase()) {
19281             case 'th':
19282                 switch(className) {
19283                     case 'switch':
19284                         this.showMode(1);
19285                         break;
19286                     case 'prev':
19287                     case 'next':
19288                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19289                         switch(this.viewMode){
19290                                 case 0:
19291                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19292                                         break;
19293                                 case 1:
19294                                 case 2:
19295                                         this.viewDate = this.moveYear(this.viewDate, dir);
19296                                         break;
19297                         }
19298                         this.fill();
19299                         break;
19300                     case 'today':
19301                         var date = new Date();
19302                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19303 //                        this.fill()
19304                         this.setValue(this.formatDate(this.date));
19305                         
19306                         this.hidePopup();
19307                         break;
19308                 }
19309                 break;
19310             case 'span':
19311                 if (className.indexOf('disabled') < 0) {
19312                     this.viewDate.setUTCDate(1);
19313                     if (className.indexOf('month') > -1) {
19314                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19315                     } else {
19316                         var year = parseInt(html, 10) || 0;
19317                         this.viewDate.setUTCFullYear(year);
19318                         
19319                     }
19320                     
19321                     if(this.singleMode){
19322                         this.setValue(this.formatDate(this.viewDate));
19323                         this.hidePopup();
19324                         return;
19325                     }
19326                     
19327                     this.showMode(-1);
19328                     this.fill();
19329                 }
19330                 break;
19331                 
19332             case 'td':
19333                 //Roo.log(className);
19334                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19335                     var day = parseInt(html, 10) || 1;
19336                     var year = this.viewDate.getUTCFullYear(),
19337                         month = this.viewDate.getUTCMonth();
19338
19339                     if (className.indexOf('old') > -1) {
19340                         if(month === 0 ){
19341                             month = 11;
19342                             year -= 1;
19343                         }else{
19344                             month -= 1;
19345                         }
19346                     } else if (className.indexOf('new') > -1) {
19347                         if (month == 11) {
19348                             month = 0;
19349                             year += 1;
19350                         } else {
19351                             month += 1;
19352                         }
19353                     }
19354                     //Roo.log([year,month,day]);
19355                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19356                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19357 //                    this.fill();
19358                     //Roo.log(this.formatDate(this.date));
19359                     this.setValue(this.formatDate(this.date));
19360                     this.hidePopup();
19361                 }
19362                 break;
19363         }
19364     },
19365     
19366     setStartDate: function(startDate)
19367     {
19368         this.startDate = startDate || -Infinity;
19369         if (this.startDate !== -Infinity) {
19370             this.startDate = this.parseDate(this.startDate);
19371         }
19372         this.update();
19373         this.updateNavArrows();
19374     },
19375
19376     setEndDate: function(endDate)
19377     {
19378         this.endDate = endDate || Infinity;
19379         if (this.endDate !== Infinity) {
19380             this.endDate = this.parseDate(this.endDate);
19381         }
19382         this.update();
19383         this.updateNavArrows();
19384     },
19385     
19386     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19387     {
19388         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19389         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19390             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19391         }
19392         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19393             return parseInt(d, 10);
19394         });
19395         this.update();
19396         this.updateNavArrows();
19397     },
19398     
19399     updateNavArrows: function() 
19400     {
19401         if(this.singleMode){
19402             return;
19403         }
19404         
19405         var d = new Date(this.viewDate),
19406         year = d.getUTCFullYear(),
19407         month = d.getUTCMonth();
19408         
19409         Roo.each(this.picker().select('.prev', true).elements, function(v){
19410             v.show();
19411             switch (this.viewMode) {
19412                 case 0:
19413
19414                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19415                         v.hide();
19416                     }
19417                     break;
19418                 case 1:
19419                 case 2:
19420                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19421                         v.hide();
19422                     }
19423                     break;
19424             }
19425         });
19426         
19427         Roo.each(this.picker().select('.next', true).elements, function(v){
19428             v.show();
19429             switch (this.viewMode) {
19430                 case 0:
19431
19432                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19433                         v.hide();
19434                     }
19435                     break;
19436                 case 1:
19437                 case 2:
19438                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19439                         v.hide();
19440                     }
19441                     break;
19442             }
19443         })
19444     },
19445     
19446     moveMonth: function(date, dir)
19447     {
19448         if (!dir) {
19449             return date;
19450         }
19451         var new_date = new Date(date.valueOf()),
19452         day = new_date.getUTCDate(),
19453         month = new_date.getUTCMonth(),
19454         mag = Math.abs(dir),
19455         new_month, test;
19456         dir = dir > 0 ? 1 : -1;
19457         if (mag == 1){
19458             test = dir == -1
19459             // If going back one month, make sure month is not current month
19460             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19461             ? function(){
19462                 return new_date.getUTCMonth() == month;
19463             }
19464             // If going forward one month, make sure month is as expected
19465             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19466             : function(){
19467                 return new_date.getUTCMonth() != new_month;
19468             };
19469             new_month = month + dir;
19470             new_date.setUTCMonth(new_month);
19471             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19472             if (new_month < 0 || new_month > 11) {
19473                 new_month = (new_month + 12) % 12;
19474             }
19475         } else {
19476             // For magnitudes >1, move one month at a time...
19477             for (var i=0; i<mag; i++) {
19478                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19479                 new_date = this.moveMonth(new_date, dir);
19480             }
19481             // ...then reset the day, keeping it in the new month
19482             new_month = new_date.getUTCMonth();
19483             new_date.setUTCDate(day);
19484             test = function(){
19485                 return new_month != new_date.getUTCMonth();
19486             };
19487         }
19488         // Common date-resetting loop -- if date is beyond end of month, make it
19489         // end of month
19490         while (test()){
19491             new_date.setUTCDate(--day);
19492             new_date.setUTCMonth(new_month);
19493         }
19494         return new_date;
19495     },
19496
19497     moveYear: function(date, dir)
19498     {
19499         return this.moveMonth(date, dir*12);
19500     },
19501
19502     dateWithinRange: function(date)
19503     {
19504         return date >= this.startDate && date <= this.endDate;
19505     },
19506
19507     
19508     remove: function() 
19509     {
19510         this.picker().remove();
19511     },
19512     
19513     validateValue : function(value)
19514     {
19515         if(this.getVisibilityEl().hasClass('hidden')){
19516             return true;
19517         }
19518         
19519         if(value.length < 1)  {
19520             if(this.allowBlank){
19521                 return true;
19522             }
19523             return false;
19524         }
19525         
19526         if(value.length < this.minLength){
19527             return false;
19528         }
19529         if(value.length > this.maxLength){
19530             return false;
19531         }
19532         if(this.vtype){
19533             var vt = Roo.form.VTypes;
19534             if(!vt[this.vtype](value, this)){
19535                 return false;
19536             }
19537         }
19538         if(typeof this.validator == "function"){
19539             var msg = this.validator(value);
19540             if(msg !== true){
19541                 return false;
19542             }
19543         }
19544         
19545         if(this.regex && !this.regex.test(value)){
19546             return false;
19547         }
19548         
19549         if(typeof(this.parseDate(value)) == 'undefined'){
19550             return false;
19551         }
19552         
19553         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19554             return false;
19555         }      
19556         
19557         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19558             return false;
19559         } 
19560         
19561         
19562         return true;
19563     },
19564     
19565     reset : function()
19566     {
19567         this.date = this.viewDate = '';
19568         
19569         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19570     }
19571    
19572 });
19573
19574 Roo.apply(Roo.bootstrap.DateField,  {
19575     
19576     head : {
19577         tag: 'thead',
19578         cn: [
19579         {
19580             tag: 'tr',
19581             cn: [
19582             {
19583                 tag: 'th',
19584                 cls: 'prev',
19585                 html: '<i class="fa fa-arrow-left"/>'
19586             },
19587             {
19588                 tag: 'th',
19589                 cls: 'switch',
19590                 colspan: '5'
19591             },
19592             {
19593                 tag: 'th',
19594                 cls: 'next',
19595                 html: '<i class="fa fa-arrow-right"/>'
19596             }
19597
19598             ]
19599         }
19600         ]
19601     },
19602     
19603     content : {
19604         tag: 'tbody',
19605         cn: [
19606         {
19607             tag: 'tr',
19608             cn: [
19609             {
19610                 tag: 'td',
19611                 colspan: '7'
19612             }
19613             ]
19614         }
19615         ]
19616     },
19617     
19618     footer : {
19619         tag: 'tfoot',
19620         cn: [
19621         {
19622             tag: 'tr',
19623             cn: [
19624             {
19625                 tag: 'th',
19626                 colspan: '7',
19627                 cls: 'today'
19628             }
19629                     
19630             ]
19631         }
19632         ]
19633     },
19634     
19635     dates:{
19636         en: {
19637             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19638             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19639             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19640             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19641             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19642             today: "Today"
19643         }
19644     },
19645     
19646     modes: [
19647     {
19648         clsName: 'days',
19649         navFnc: 'Month',
19650         navStep: 1
19651     },
19652     {
19653         clsName: 'months',
19654         navFnc: 'FullYear',
19655         navStep: 1
19656     },
19657     {
19658         clsName: 'years',
19659         navFnc: 'FullYear',
19660         navStep: 10
19661     }]
19662 });
19663
19664 Roo.apply(Roo.bootstrap.DateField,  {
19665   
19666     template : {
19667         tag: 'div',
19668         cls: 'datepicker dropdown-menu roo-dynamic',
19669         cn: [
19670         {
19671             tag: 'div',
19672             cls: 'datepicker-days',
19673             cn: [
19674             {
19675                 tag: 'table',
19676                 cls: 'table-condensed',
19677                 cn:[
19678                 Roo.bootstrap.DateField.head,
19679                 {
19680                     tag: 'tbody'
19681                 },
19682                 Roo.bootstrap.DateField.footer
19683                 ]
19684             }
19685             ]
19686         },
19687         {
19688             tag: 'div',
19689             cls: 'datepicker-months',
19690             cn: [
19691             {
19692                 tag: 'table',
19693                 cls: 'table-condensed',
19694                 cn:[
19695                 Roo.bootstrap.DateField.head,
19696                 Roo.bootstrap.DateField.content,
19697                 Roo.bootstrap.DateField.footer
19698                 ]
19699             }
19700             ]
19701         },
19702         {
19703             tag: 'div',
19704             cls: 'datepicker-years',
19705             cn: [
19706             {
19707                 tag: 'table',
19708                 cls: 'table-condensed',
19709                 cn:[
19710                 Roo.bootstrap.DateField.head,
19711                 Roo.bootstrap.DateField.content,
19712                 Roo.bootstrap.DateField.footer
19713                 ]
19714             }
19715             ]
19716         }
19717         ]
19718     }
19719 });
19720
19721  
19722
19723  /*
19724  * - LGPL
19725  *
19726  * TimeField
19727  * 
19728  */
19729
19730 /**
19731  * @class Roo.bootstrap.TimeField
19732  * @extends Roo.bootstrap.Input
19733  * Bootstrap DateField class
19734  * 
19735  * 
19736  * @constructor
19737  * Create a new TimeField
19738  * @param {Object} config The config object
19739  */
19740
19741 Roo.bootstrap.TimeField = function(config){
19742     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19743     this.addEvents({
19744             /**
19745              * @event show
19746              * Fires when this field show.
19747              * @param {Roo.bootstrap.DateField} thisthis
19748              * @param {Mixed} date The date value
19749              */
19750             show : true,
19751             /**
19752              * @event show
19753              * Fires when this field hide.
19754              * @param {Roo.bootstrap.DateField} this
19755              * @param {Mixed} date The date value
19756              */
19757             hide : true,
19758             /**
19759              * @event select
19760              * Fires when select a date.
19761              * @param {Roo.bootstrap.DateField} this
19762              * @param {Mixed} date The date value
19763              */
19764             select : true
19765         });
19766 };
19767
19768 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19769     
19770     /**
19771      * @cfg {String} format
19772      * The default time format string which can be overriden for localization support.  The format must be
19773      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19774      */
19775     format : "H:i",
19776        
19777     onRender: function(ct, position)
19778     {
19779         
19780         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19781                 
19782         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19783         
19784         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19785         
19786         this.pop = this.picker().select('>.datepicker-time',true).first();
19787         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19788         
19789         this.picker().on('mousedown', this.onMousedown, this);
19790         this.picker().on('click', this.onClick, this);
19791         
19792         this.picker().addClass('datepicker-dropdown');
19793     
19794         this.fillTime();
19795         this.update();
19796             
19797         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19798         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19799         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19800         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19801         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19802         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19803
19804     },
19805     
19806     fireKey: function(e){
19807         if (!this.picker().isVisible()){
19808             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19809                 this.show();
19810             }
19811             return;
19812         }
19813
19814         e.preventDefault();
19815         
19816         switch(e.keyCode){
19817             case 27: // escape
19818                 this.hide();
19819                 break;
19820             case 37: // left
19821             case 39: // right
19822                 this.onTogglePeriod();
19823                 break;
19824             case 38: // up
19825                 this.onIncrementMinutes();
19826                 break;
19827             case 40: // down
19828                 this.onDecrementMinutes();
19829                 break;
19830             case 13: // enter
19831             case 9: // tab
19832                 this.setTime();
19833                 break;
19834         }
19835     },
19836     
19837     onClick: function(e) {
19838         e.stopPropagation();
19839         e.preventDefault();
19840     },
19841     
19842     picker : function()
19843     {
19844         return this.el.select('.datepicker', true).first();
19845     },
19846     
19847     fillTime: function()
19848     {    
19849         var time = this.pop.select('tbody', true).first();
19850         
19851         time.dom.innerHTML = '';
19852         
19853         time.createChild({
19854             tag: 'tr',
19855             cn: [
19856                 {
19857                     tag: 'td',
19858                     cn: [
19859                         {
19860                             tag: 'a',
19861                             href: '#',
19862                             cls: 'btn',
19863                             cn: [
19864                                 {
19865                                     tag: 'span',
19866                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19867                                 }
19868                             ]
19869                         } 
19870                     ]
19871                 },
19872                 {
19873                     tag: 'td',
19874                     cls: 'separator'
19875                 },
19876                 {
19877                     tag: 'td',
19878                     cn: [
19879                         {
19880                             tag: 'a',
19881                             href: '#',
19882                             cls: 'btn',
19883                             cn: [
19884                                 {
19885                                     tag: 'span',
19886                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19887                                 }
19888                             ]
19889                         }
19890                     ]
19891                 },
19892                 {
19893                     tag: 'td',
19894                     cls: 'separator'
19895                 }
19896             ]
19897         });
19898         
19899         time.createChild({
19900             tag: 'tr',
19901             cn: [
19902                 {
19903                     tag: 'td',
19904                     cn: [
19905                         {
19906                             tag: 'span',
19907                             cls: 'timepicker-hour',
19908                             html: '00'
19909                         }  
19910                     ]
19911                 },
19912                 {
19913                     tag: 'td',
19914                     cls: 'separator',
19915                     html: ':'
19916                 },
19917                 {
19918                     tag: 'td',
19919                     cn: [
19920                         {
19921                             tag: 'span',
19922                             cls: 'timepicker-minute',
19923                             html: '00'
19924                         }  
19925                     ]
19926                 },
19927                 {
19928                     tag: 'td',
19929                     cls: 'separator'
19930                 },
19931                 {
19932                     tag: 'td',
19933                     cn: [
19934                         {
19935                             tag: 'button',
19936                             type: 'button',
19937                             cls: 'btn btn-primary period',
19938                             html: 'AM'
19939                             
19940                         }
19941                     ]
19942                 }
19943             ]
19944         });
19945         
19946         time.createChild({
19947             tag: 'tr',
19948             cn: [
19949                 {
19950                     tag: 'td',
19951                     cn: [
19952                         {
19953                             tag: 'a',
19954                             href: '#',
19955                             cls: 'btn',
19956                             cn: [
19957                                 {
19958                                     tag: 'span',
19959                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19960                                 }
19961                             ]
19962                         }
19963                     ]
19964                 },
19965                 {
19966                     tag: 'td',
19967                     cls: 'separator'
19968                 },
19969                 {
19970                     tag: 'td',
19971                     cn: [
19972                         {
19973                             tag: 'a',
19974                             href: '#',
19975                             cls: 'btn',
19976                             cn: [
19977                                 {
19978                                     tag: 'span',
19979                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19980                                 }
19981                             ]
19982                         }
19983                     ]
19984                 },
19985                 {
19986                     tag: 'td',
19987                     cls: 'separator'
19988                 }
19989             ]
19990         });
19991         
19992     },
19993     
19994     update: function()
19995     {
19996         
19997         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19998         
19999         this.fill();
20000     },
20001     
20002     fill: function() 
20003     {
20004         var hours = this.time.getHours();
20005         var minutes = this.time.getMinutes();
20006         var period = 'AM';
20007         
20008         if(hours > 11){
20009             period = 'PM';
20010         }
20011         
20012         if(hours == 0){
20013             hours = 12;
20014         }
20015         
20016         
20017         if(hours > 12){
20018             hours = hours - 12;
20019         }
20020         
20021         if(hours < 10){
20022             hours = '0' + hours;
20023         }
20024         
20025         if(minutes < 10){
20026             minutes = '0' + minutes;
20027         }
20028         
20029         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20030         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20031         this.pop.select('button', true).first().dom.innerHTML = period;
20032         
20033     },
20034     
20035     place: function()
20036     {   
20037         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20038         
20039         var cls = ['bottom'];
20040         
20041         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20042             cls.pop();
20043             cls.push('top');
20044         }
20045         
20046         cls.push('right');
20047         
20048         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20049             cls.pop();
20050             cls.push('left');
20051         }
20052         
20053         this.picker().addClass(cls.join('-'));
20054         
20055         var _this = this;
20056         
20057         Roo.each(cls, function(c){
20058             if(c == 'bottom'){
20059                 _this.picker().setTop(_this.inputEl().getHeight());
20060                 return;
20061             }
20062             if(c == 'top'){
20063                 _this.picker().setTop(0 - _this.picker().getHeight());
20064                 return;
20065             }
20066             
20067             if(c == 'left'){
20068                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20069                 return;
20070             }
20071             if(c == 'right'){
20072                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20073                 return;
20074             }
20075         });
20076         
20077     },
20078   
20079     onFocus : function()
20080     {
20081         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20082         this.show();
20083     },
20084     
20085     onBlur : function()
20086     {
20087         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20088         this.hide();
20089     },
20090     
20091     show : function()
20092     {
20093         this.picker().show();
20094         this.pop.show();
20095         this.update();
20096         this.place();
20097         
20098         this.fireEvent('show', this, this.date);
20099     },
20100     
20101     hide : function()
20102     {
20103         this.picker().hide();
20104         this.pop.hide();
20105         
20106         this.fireEvent('hide', this, this.date);
20107     },
20108     
20109     setTime : function()
20110     {
20111         this.hide();
20112         this.setValue(this.time.format(this.format));
20113         
20114         this.fireEvent('select', this, this.date);
20115         
20116         
20117     },
20118     
20119     onMousedown: function(e){
20120         e.stopPropagation();
20121         e.preventDefault();
20122     },
20123     
20124     onIncrementHours: function()
20125     {
20126         Roo.log('onIncrementHours');
20127         this.time = this.time.add(Date.HOUR, 1);
20128         this.update();
20129         
20130     },
20131     
20132     onDecrementHours: function()
20133     {
20134         Roo.log('onDecrementHours');
20135         this.time = this.time.add(Date.HOUR, -1);
20136         this.update();
20137     },
20138     
20139     onIncrementMinutes: function()
20140     {
20141         Roo.log('onIncrementMinutes');
20142         this.time = this.time.add(Date.MINUTE, 1);
20143         this.update();
20144     },
20145     
20146     onDecrementMinutes: function()
20147     {
20148         Roo.log('onDecrementMinutes');
20149         this.time = this.time.add(Date.MINUTE, -1);
20150         this.update();
20151     },
20152     
20153     onTogglePeriod: function()
20154     {
20155         Roo.log('onTogglePeriod');
20156         this.time = this.time.add(Date.HOUR, 12);
20157         this.update();
20158     }
20159     
20160    
20161 });
20162
20163 Roo.apply(Roo.bootstrap.TimeField,  {
20164     
20165     content : {
20166         tag: 'tbody',
20167         cn: [
20168             {
20169                 tag: 'tr',
20170                 cn: [
20171                 {
20172                     tag: 'td',
20173                     colspan: '7'
20174                 }
20175                 ]
20176             }
20177         ]
20178     },
20179     
20180     footer : {
20181         tag: 'tfoot',
20182         cn: [
20183             {
20184                 tag: 'tr',
20185                 cn: [
20186                 {
20187                     tag: 'th',
20188                     colspan: '7',
20189                     cls: '',
20190                     cn: [
20191                         {
20192                             tag: 'button',
20193                             cls: 'btn btn-info ok',
20194                             html: 'OK'
20195                         }
20196                     ]
20197                 }
20198
20199                 ]
20200             }
20201         ]
20202     }
20203 });
20204
20205 Roo.apply(Roo.bootstrap.TimeField,  {
20206   
20207     template : {
20208         tag: 'div',
20209         cls: 'datepicker dropdown-menu',
20210         cn: [
20211             {
20212                 tag: 'div',
20213                 cls: 'datepicker-time',
20214                 cn: [
20215                 {
20216                     tag: 'table',
20217                     cls: 'table-condensed',
20218                     cn:[
20219                     Roo.bootstrap.TimeField.content,
20220                     Roo.bootstrap.TimeField.footer
20221                     ]
20222                 }
20223                 ]
20224             }
20225         ]
20226     }
20227 });
20228
20229  
20230
20231  /*
20232  * - LGPL
20233  *
20234  * MonthField
20235  * 
20236  */
20237
20238 /**
20239  * @class Roo.bootstrap.MonthField
20240  * @extends Roo.bootstrap.Input
20241  * Bootstrap MonthField class
20242  * 
20243  * @cfg {String} language default en
20244  * 
20245  * @constructor
20246  * Create a new MonthField
20247  * @param {Object} config The config object
20248  */
20249
20250 Roo.bootstrap.MonthField = function(config){
20251     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20252     
20253     this.addEvents({
20254         /**
20255          * @event show
20256          * Fires when this field show.
20257          * @param {Roo.bootstrap.MonthField} this
20258          * @param {Mixed} date The date value
20259          */
20260         show : true,
20261         /**
20262          * @event show
20263          * Fires when this field hide.
20264          * @param {Roo.bootstrap.MonthField} this
20265          * @param {Mixed} date The date value
20266          */
20267         hide : true,
20268         /**
20269          * @event select
20270          * Fires when select a date.
20271          * @param {Roo.bootstrap.MonthField} this
20272          * @param {String} oldvalue The old value
20273          * @param {String} newvalue The new value
20274          */
20275         select : true
20276     });
20277 };
20278
20279 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20280     
20281     onRender: function(ct, position)
20282     {
20283         
20284         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20285         
20286         this.language = this.language || 'en';
20287         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20288         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20289         
20290         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20291         this.isInline = false;
20292         this.isInput = true;
20293         this.component = this.el.select('.add-on', true).first() || false;
20294         this.component = (this.component && this.component.length === 0) ? false : this.component;
20295         this.hasInput = this.component && this.inputEL().length;
20296         
20297         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20298         
20299         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20300         
20301         this.picker().on('mousedown', this.onMousedown, this);
20302         this.picker().on('click', this.onClick, this);
20303         
20304         this.picker().addClass('datepicker-dropdown');
20305         
20306         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20307             v.setStyle('width', '189px');
20308         });
20309         
20310         this.fillMonths();
20311         
20312         this.update();
20313         
20314         if(this.isInline) {
20315             this.show();
20316         }
20317         
20318     },
20319     
20320     setValue: function(v, suppressEvent)
20321     {   
20322         var o = this.getValue();
20323         
20324         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20325         
20326         this.update();
20327
20328         if(suppressEvent !== true){
20329             this.fireEvent('select', this, o, v);
20330         }
20331         
20332     },
20333     
20334     getValue: function()
20335     {
20336         return this.value;
20337     },
20338     
20339     onClick: function(e) 
20340     {
20341         e.stopPropagation();
20342         e.preventDefault();
20343         
20344         var target = e.getTarget();
20345         
20346         if(target.nodeName.toLowerCase() === 'i'){
20347             target = Roo.get(target).dom.parentNode;
20348         }
20349         
20350         var nodeName = target.nodeName;
20351         var className = target.className;
20352         var html = target.innerHTML;
20353         
20354         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20355             return;
20356         }
20357         
20358         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20359         
20360         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20361         
20362         this.hide();
20363                         
20364     },
20365     
20366     picker : function()
20367     {
20368         return this.pickerEl;
20369     },
20370     
20371     fillMonths: function()
20372     {    
20373         var i = 0;
20374         var months = this.picker().select('>.datepicker-months td', true).first();
20375         
20376         months.dom.innerHTML = '';
20377         
20378         while (i < 12) {
20379             var month = {
20380                 tag: 'span',
20381                 cls: 'month',
20382                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20383             };
20384             
20385             months.createChild(month);
20386         }
20387         
20388     },
20389     
20390     update: function()
20391     {
20392         var _this = this;
20393         
20394         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20395             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20396         }
20397         
20398         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20399             e.removeClass('active');
20400             
20401             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20402                 e.addClass('active');
20403             }
20404         })
20405     },
20406     
20407     place: function()
20408     {
20409         if(this.isInline) {
20410             return;
20411         }
20412         
20413         this.picker().removeClass(['bottom', 'top']);
20414         
20415         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20416             /*
20417              * place to the top of element!
20418              *
20419              */
20420             
20421             this.picker().addClass('top');
20422             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20423             
20424             return;
20425         }
20426         
20427         this.picker().addClass('bottom');
20428         
20429         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20430     },
20431     
20432     onFocus : function()
20433     {
20434         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20435         this.show();
20436     },
20437     
20438     onBlur : function()
20439     {
20440         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20441         
20442         var d = this.inputEl().getValue();
20443         
20444         this.setValue(d);
20445                 
20446         this.hide();
20447     },
20448     
20449     show : function()
20450     {
20451         this.picker().show();
20452         this.picker().select('>.datepicker-months', true).first().show();
20453         this.update();
20454         this.place();
20455         
20456         this.fireEvent('show', this, this.date);
20457     },
20458     
20459     hide : function()
20460     {
20461         if(this.isInline) {
20462             return;
20463         }
20464         this.picker().hide();
20465         this.fireEvent('hide', this, this.date);
20466         
20467     },
20468     
20469     onMousedown: function(e)
20470     {
20471         e.stopPropagation();
20472         e.preventDefault();
20473     },
20474     
20475     keyup: function(e)
20476     {
20477         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20478         this.update();
20479     },
20480
20481     fireKey: function(e)
20482     {
20483         if (!this.picker().isVisible()){
20484             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20485                 this.show();
20486             }
20487             return;
20488         }
20489         
20490         var dir;
20491         
20492         switch(e.keyCode){
20493             case 27: // escape
20494                 this.hide();
20495                 e.preventDefault();
20496                 break;
20497             case 37: // left
20498             case 39: // right
20499                 dir = e.keyCode == 37 ? -1 : 1;
20500                 
20501                 this.vIndex = this.vIndex + dir;
20502                 
20503                 if(this.vIndex < 0){
20504                     this.vIndex = 0;
20505                 }
20506                 
20507                 if(this.vIndex > 11){
20508                     this.vIndex = 11;
20509                 }
20510                 
20511                 if(isNaN(this.vIndex)){
20512                     this.vIndex = 0;
20513                 }
20514                 
20515                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20516                 
20517                 break;
20518             case 38: // up
20519             case 40: // down
20520                 
20521                 dir = e.keyCode == 38 ? -1 : 1;
20522                 
20523                 this.vIndex = this.vIndex + dir * 4;
20524                 
20525                 if(this.vIndex < 0){
20526                     this.vIndex = 0;
20527                 }
20528                 
20529                 if(this.vIndex > 11){
20530                     this.vIndex = 11;
20531                 }
20532                 
20533                 if(isNaN(this.vIndex)){
20534                     this.vIndex = 0;
20535                 }
20536                 
20537                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20538                 break;
20539                 
20540             case 13: // enter
20541                 
20542                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20543                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20544                 }
20545                 
20546                 this.hide();
20547                 e.preventDefault();
20548                 break;
20549             case 9: // tab
20550                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20551                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20552                 }
20553                 this.hide();
20554                 break;
20555             case 16: // shift
20556             case 17: // ctrl
20557             case 18: // alt
20558                 break;
20559             default :
20560                 this.hide();
20561                 
20562         }
20563     },
20564     
20565     remove: function() 
20566     {
20567         this.picker().remove();
20568     }
20569    
20570 });
20571
20572 Roo.apply(Roo.bootstrap.MonthField,  {
20573     
20574     content : {
20575         tag: 'tbody',
20576         cn: [
20577         {
20578             tag: 'tr',
20579             cn: [
20580             {
20581                 tag: 'td',
20582                 colspan: '7'
20583             }
20584             ]
20585         }
20586         ]
20587     },
20588     
20589     dates:{
20590         en: {
20591             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20592             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20593         }
20594     }
20595 });
20596
20597 Roo.apply(Roo.bootstrap.MonthField,  {
20598   
20599     template : {
20600         tag: 'div',
20601         cls: 'datepicker dropdown-menu roo-dynamic',
20602         cn: [
20603             {
20604                 tag: 'div',
20605                 cls: 'datepicker-months',
20606                 cn: [
20607                 {
20608                     tag: 'table',
20609                     cls: 'table-condensed',
20610                     cn:[
20611                         Roo.bootstrap.DateField.content
20612                     ]
20613                 }
20614                 ]
20615             }
20616         ]
20617     }
20618 });
20619
20620  
20621
20622  
20623  /*
20624  * - LGPL
20625  *
20626  * CheckBox
20627  * 
20628  */
20629
20630 /**
20631  * @class Roo.bootstrap.CheckBox
20632  * @extends Roo.bootstrap.Input
20633  * Bootstrap CheckBox class
20634  * 
20635  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20636  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20637  * @cfg {String} boxLabel The text that appears beside the checkbox
20638  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20639  * @cfg {Boolean} checked initnal the element
20640  * @cfg {Boolean} inline inline the element (default false)
20641  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20642  * @cfg {String} tooltip label tooltip
20643  * 
20644  * @constructor
20645  * Create a new CheckBox
20646  * @param {Object} config The config object
20647  */
20648
20649 Roo.bootstrap.CheckBox = function(config){
20650     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20651    
20652     this.addEvents({
20653         /**
20654         * @event check
20655         * Fires when the element is checked or unchecked.
20656         * @param {Roo.bootstrap.CheckBox} this This input
20657         * @param {Boolean} checked The new checked value
20658         */
20659        check : true,
20660        /**
20661         * @event click
20662         * Fires when the element is click.
20663         * @param {Roo.bootstrap.CheckBox} this This input
20664         */
20665        click : true
20666     });
20667     
20668 };
20669
20670 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20671   
20672     inputType: 'checkbox',
20673     inputValue: 1,
20674     valueOff: 0,
20675     boxLabel: false,
20676     checked: false,
20677     weight : false,
20678     inline: false,
20679     tooltip : '',
20680     
20681     getAutoCreate : function()
20682     {
20683         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20684         
20685         var id = Roo.id();
20686         
20687         var cfg = {};
20688         
20689         cfg.cls = 'form-group ' + this.inputType; //input-group
20690         
20691         if(this.inline){
20692             cfg.cls += ' ' + this.inputType + '-inline';
20693         }
20694         
20695         var input =  {
20696             tag: 'input',
20697             id : id,
20698             type : this.inputType,
20699             value : this.inputValue,
20700             cls : 'roo-' + this.inputType, //'form-box',
20701             placeholder : this.placeholder || ''
20702             
20703         };
20704         
20705         if(this.inputType != 'radio'){
20706             var hidden =  {
20707                 tag: 'input',
20708                 type : 'hidden',
20709                 cls : 'roo-hidden-value',
20710                 value : this.checked ? this.inputValue : this.valueOff
20711             };
20712         }
20713         
20714             
20715         if (this.weight) { // Validity check?
20716             cfg.cls += " " + this.inputType + "-" + this.weight;
20717         }
20718         
20719         if (this.disabled) {
20720             input.disabled=true;
20721         }
20722         
20723         if(this.checked){
20724             input.checked = this.checked;
20725         }
20726         
20727         if (this.name) {
20728             
20729             input.name = this.name;
20730             
20731             if(this.inputType != 'radio'){
20732                 hidden.name = this.name;
20733                 input.name = '_hidden_' + this.name;
20734             }
20735         }
20736         
20737         if (this.size) {
20738             input.cls += ' input-' + this.size;
20739         }
20740         
20741         var settings=this;
20742         
20743         ['xs','sm','md','lg'].map(function(size){
20744             if (settings[size]) {
20745                 cfg.cls += ' col-' + size + '-' + settings[size];
20746             }
20747         });
20748         
20749         var inputblock = input;
20750          
20751         if (this.before || this.after) {
20752             
20753             inputblock = {
20754                 cls : 'input-group',
20755                 cn :  [] 
20756             };
20757             
20758             if (this.before) {
20759                 inputblock.cn.push({
20760                     tag :'span',
20761                     cls : 'input-group-addon',
20762                     html : this.before
20763                 });
20764             }
20765             
20766             inputblock.cn.push(input);
20767             
20768             if(this.inputType != 'radio'){
20769                 inputblock.cn.push(hidden);
20770             }
20771             
20772             if (this.after) {
20773                 inputblock.cn.push({
20774                     tag :'span',
20775                     cls : 'input-group-addon',
20776                     html : this.after
20777                 });
20778             }
20779             
20780         }
20781         
20782         if (align ==='left' && this.fieldLabel.length) {
20783 //                Roo.log("left and has label");
20784             cfg.cn = [
20785                 {
20786                     tag: 'label',
20787                     'for' :  id,
20788                     cls : 'control-label',
20789                     html : this.fieldLabel
20790                 },
20791                 {
20792                     cls : "", 
20793                     cn: [
20794                         inputblock
20795                     ]
20796                 }
20797             ];
20798             
20799             if(this.labelWidth > 12){
20800                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20801             }
20802             
20803             if(this.labelWidth < 13 && this.labelmd == 0){
20804                 this.labelmd = this.labelWidth;
20805             }
20806             
20807             if(this.labellg > 0){
20808                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20809                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20810             }
20811             
20812             if(this.labelmd > 0){
20813                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20814                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20815             }
20816             
20817             if(this.labelsm > 0){
20818                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20819                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20820             }
20821             
20822             if(this.labelxs > 0){
20823                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20824                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20825             }
20826             
20827         } else if ( this.fieldLabel.length) {
20828 //                Roo.log(" label");
20829                 cfg.cn = [
20830                    
20831                     {
20832                         tag: this.boxLabel ? 'span' : 'label',
20833                         'for': id,
20834                         cls: 'control-label box-input-label',
20835                         //cls : 'input-group-addon',
20836                         html : this.fieldLabel
20837                     },
20838                     
20839                     inputblock
20840                     
20841                 ];
20842
20843         } else {
20844             
20845 //                Roo.log(" no label && no align");
20846                 cfg.cn = [  inputblock ] ;
20847                 
20848                 
20849         }
20850         
20851         if(this.boxLabel){
20852              var boxLabelCfg = {
20853                 tag: 'label',
20854                 //'for': id, // box label is handled by onclick - so no for...
20855                 cls: 'box-label',
20856                 html: this.boxLabel
20857             };
20858             
20859             if(this.tooltip){
20860                 boxLabelCfg.tooltip = this.tooltip;
20861             }
20862              
20863             cfg.cn.push(boxLabelCfg);
20864         }
20865         
20866         if(this.inputType != 'radio'){
20867             cfg.cn.push(hidden);
20868         }
20869         
20870         return cfg;
20871         
20872     },
20873     
20874     /**
20875      * return the real input element.
20876      */
20877     inputEl: function ()
20878     {
20879         return this.el.select('input.roo-' + this.inputType,true).first();
20880     },
20881     hiddenEl: function ()
20882     {
20883         return this.el.select('input.roo-hidden-value',true).first();
20884     },
20885     
20886     labelEl: function()
20887     {
20888         return this.el.select('label.control-label',true).first();
20889     },
20890     /* depricated... */
20891     
20892     label: function()
20893     {
20894         return this.labelEl();
20895     },
20896     
20897     boxLabelEl: function()
20898     {
20899         return this.el.select('label.box-label',true).first();
20900     },
20901     
20902     initEvents : function()
20903     {
20904 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20905         
20906         this.inputEl().on('click', this.onClick,  this);
20907         
20908         if (this.boxLabel) { 
20909             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20910         }
20911         
20912         this.startValue = this.getValue();
20913         
20914         if(this.groupId){
20915             Roo.bootstrap.CheckBox.register(this);
20916         }
20917     },
20918     
20919     onClick : function(e)
20920     {   
20921         if(this.fireEvent('click', this, e) !== false){
20922             this.setChecked(!this.checked);
20923         }
20924         
20925     },
20926     
20927     setChecked : function(state,suppressEvent)
20928     {
20929         this.startValue = this.getValue();
20930
20931         if(this.inputType == 'radio'){
20932             
20933             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20934                 e.dom.checked = false;
20935             });
20936             
20937             this.inputEl().dom.checked = true;
20938             
20939             this.inputEl().dom.value = this.inputValue;
20940             
20941             if(suppressEvent !== true){
20942                 this.fireEvent('check', this, true);
20943             }
20944             
20945             this.validate();
20946             
20947             return;
20948         }
20949         
20950         this.checked = state;
20951         
20952         this.inputEl().dom.checked = state;
20953         
20954         
20955         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20956         
20957         if(suppressEvent !== true){
20958             this.fireEvent('check', this, state);
20959         }
20960         
20961         this.validate();
20962     },
20963     
20964     getValue : function()
20965     {
20966         if(this.inputType == 'radio'){
20967             return this.getGroupValue();
20968         }
20969         
20970         return this.hiddenEl().dom.value;
20971         
20972     },
20973     
20974     getGroupValue : function()
20975     {
20976         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20977             return '';
20978         }
20979         
20980         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20981     },
20982     
20983     setValue : function(v,suppressEvent)
20984     {
20985         if(this.inputType == 'radio'){
20986             this.setGroupValue(v, suppressEvent);
20987             return;
20988         }
20989         
20990         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20991         
20992         this.validate();
20993     },
20994     
20995     setGroupValue : function(v, suppressEvent)
20996     {
20997         this.startValue = this.getValue();
20998         
20999         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21000             e.dom.checked = false;
21001             
21002             if(e.dom.value == v){
21003                 e.dom.checked = true;
21004             }
21005         });
21006         
21007         if(suppressEvent !== true){
21008             this.fireEvent('check', this, true);
21009         }
21010
21011         this.validate();
21012         
21013         return;
21014     },
21015     
21016     validate : function()
21017     {
21018         if(this.getVisibilityEl().hasClass('hidden')){
21019             return true;
21020         }
21021         
21022         if(
21023                 this.disabled || 
21024                 (this.inputType == 'radio' && this.validateRadio()) ||
21025                 (this.inputType == 'checkbox' && this.validateCheckbox())
21026         ){
21027             this.markValid();
21028             return true;
21029         }
21030         
21031         this.markInvalid();
21032         return false;
21033     },
21034     
21035     validateRadio : function()
21036     {
21037         if(this.getVisibilityEl().hasClass('hidden')){
21038             return true;
21039         }
21040         
21041         if(this.allowBlank){
21042             return true;
21043         }
21044         
21045         var valid = false;
21046         
21047         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21048             if(!e.dom.checked){
21049                 return;
21050             }
21051             
21052             valid = true;
21053             
21054             return false;
21055         });
21056         
21057         return valid;
21058     },
21059     
21060     validateCheckbox : function()
21061     {
21062         if(!this.groupId){
21063             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21064             //return (this.getValue() == this.inputValue) ? true : false;
21065         }
21066         
21067         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21068         
21069         if(!group){
21070             return false;
21071         }
21072         
21073         var r = false;
21074         
21075         for(var i in group){
21076             if(group[i].el.isVisible(true)){
21077                 r = false;
21078                 break;
21079             }
21080             
21081             r = true;
21082         }
21083         
21084         for(var i in group){
21085             if(r){
21086                 break;
21087             }
21088             
21089             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21090         }
21091         
21092         return r;
21093     },
21094     
21095     /**
21096      * Mark this field as valid
21097      */
21098     markValid : function()
21099     {
21100         var _this = this;
21101         
21102         this.fireEvent('valid', this);
21103         
21104         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21105         
21106         if(this.groupId){
21107             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21108         }
21109         
21110         if(label){
21111             label.markValid();
21112         }
21113
21114         if(this.inputType == 'radio'){
21115             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21116                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21117                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21118             });
21119             
21120             return;
21121         }
21122
21123         if(!this.groupId){
21124             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21125             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21126             return;
21127         }
21128         
21129         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21130         
21131         if(!group){
21132             return;
21133         }
21134         
21135         for(var i in group){
21136             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21137             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21138         }
21139     },
21140     
21141      /**
21142      * Mark this field as invalid
21143      * @param {String} msg The validation message
21144      */
21145     markInvalid : function(msg)
21146     {
21147         if(this.allowBlank){
21148             return;
21149         }
21150         
21151         var _this = this;
21152         
21153         this.fireEvent('invalid', this, msg);
21154         
21155         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21156         
21157         if(this.groupId){
21158             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21159         }
21160         
21161         if(label){
21162             label.markInvalid();
21163         }
21164             
21165         if(this.inputType == 'radio'){
21166             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21167                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21168                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21169             });
21170             
21171             return;
21172         }
21173         
21174         if(!this.groupId){
21175             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21176             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21177             return;
21178         }
21179         
21180         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21181         
21182         if(!group){
21183             return;
21184         }
21185         
21186         for(var i in group){
21187             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21188             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21189         }
21190         
21191     },
21192     
21193     clearInvalid : function()
21194     {
21195         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21196         
21197         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21198         
21199         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21200         
21201         if (label && label.iconEl) {
21202             label.iconEl.removeClass(label.validClass);
21203             label.iconEl.removeClass(label.invalidClass);
21204         }
21205     },
21206     
21207     disable : function()
21208     {
21209         if(this.inputType != 'radio'){
21210             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21211             return;
21212         }
21213         
21214         var _this = this;
21215         
21216         if(this.rendered){
21217             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21218                 _this.getActionEl().addClass(this.disabledClass);
21219                 e.dom.disabled = true;
21220             });
21221         }
21222         
21223         this.disabled = true;
21224         this.fireEvent("disable", this);
21225         return this;
21226     },
21227
21228     enable : function()
21229     {
21230         if(this.inputType != 'radio'){
21231             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21232             return;
21233         }
21234         
21235         var _this = this;
21236         
21237         if(this.rendered){
21238             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21239                 _this.getActionEl().removeClass(this.disabledClass);
21240                 e.dom.disabled = false;
21241             });
21242         }
21243         
21244         this.disabled = false;
21245         this.fireEvent("enable", this);
21246         return this;
21247     },
21248     
21249     setBoxLabel : function(v)
21250     {
21251         this.boxLabel = v;
21252         
21253         if(this.rendered){
21254             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21255         }
21256     }
21257
21258 });
21259
21260 Roo.apply(Roo.bootstrap.CheckBox, {
21261     
21262     groups: {},
21263     
21264      /**
21265     * register a CheckBox Group
21266     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21267     */
21268     register : function(checkbox)
21269     {
21270         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21271             this.groups[checkbox.groupId] = {};
21272         }
21273         
21274         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21275             return;
21276         }
21277         
21278         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21279         
21280     },
21281     /**
21282     * fetch a CheckBox Group based on the group ID
21283     * @param {string} the group ID
21284     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21285     */
21286     get: function(groupId) {
21287         if (typeof(this.groups[groupId]) == 'undefined') {
21288             return false;
21289         }
21290         
21291         return this.groups[groupId] ;
21292     }
21293     
21294     
21295 });
21296 /*
21297  * - LGPL
21298  *
21299  * RadioItem
21300  * 
21301  */
21302
21303 /**
21304  * @class Roo.bootstrap.Radio
21305  * @extends Roo.bootstrap.Component
21306  * Bootstrap Radio class
21307  * @cfg {String} boxLabel - the label associated
21308  * @cfg {String} value - the value of radio
21309  * 
21310  * @constructor
21311  * Create a new Radio
21312  * @param {Object} config The config object
21313  */
21314 Roo.bootstrap.Radio = function(config){
21315     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21316     
21317 };
21318
21319 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21320     
21321     boxLabel : '',
21322     
21323     value : '',
21324     
21325     getAutoCreate : function()
21326     {
21327         var cfg = {
21328             tag : 'div',
21329             cls : 'form-group radio',
21330             cn : [
21331                 {
21332                     tag : 'label',
21333                     cls : 'box-label',
21334                     html : this.boxLabel
21335                 }
21336             ]
21337         };
21338         
21339         return cfg;
21340     },
21341     
21342     initEvents : function() 
21343     {
21344         this.parent().register(this);
21345         
21346         this.el.on('click', this.onClick, this);
21347         
21348     },
21349     
21350     onClick : function(e)
21351     {
21352         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21353             this.setChecked(true);
21354         }
21355     },
21356     
21357     setChecked : function(state, suppressEvent)
21358     {
21359         this.parent().setValue(this.value, suppressEvent);
21360         
21361     },
21362     
21363     setBoxLabel : function(v)
21364     {
21365         this.boxLabel = v;
21366         
21367         if(this.rendered){
21368             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21369         }
21370     }
21371     
21372 });
21373  
21374
21375  /*
21376  * - LGPL
21377  *
21378  * Input
21379  * 
21380  */
21381
21382 /**
21383  * @class Roo.bootstrap.SecurePass
21384  * @extends Roo.bootstrap.Input
21385  * Bootstrap SecurePass class
21386  *
21387  * 
21388  * @constructor
21389  * Create a new SecurePass
21390  * @param {Object} config The config object
21391  */
21392  
21393 Roo.bootstrap.SecurePass = function (config) {
21394     // these go here, so the translation tool can replace them..
21395     this.errors = {
21396         PwdEmpty: "Please type a password, and then retype it to confirm.",
21397         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21398         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21399         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21400         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21401         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21402         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21403         TooWeak: "Your password is Too Weak."
21404     },
21405     this.meterLabel = "Password strength:";
21406     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21407     this.meterClass = [
21408         "roo-password-meter-tooweak", 
21409         "roo-password-meter-weak", 
21410         "roo-password-meter-medium", 
21411         "roo-password-meter-strong", 
21412         "roo-password-meter-grey"
21413     ];
21414     
21415     this.errors = {};
21416     
21417     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21418 }
21419
21420 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21421     /**
21422      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21423      * {
21424      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21425      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21426      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21427      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21428      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21429      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21430      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21431      * })
21432      */
21433     // private
21434     
21435     meterWidth: 300,
21436     errorMsg :'',    
21437     errors: false,
21438     imageRoot: '/',
21439     /**
21440      * @cfg {String/Object} Label for the strength meter (defaults to
21441      * 'Password strength:')
21442      */
21443     // private
21444     meterLabel: '',
21445     /**
21446      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21447      * ['Weak', 'Medium', 'Strong'])
21448      */
21449     // private    
21450     pwdStrengths: false,    
21451     // private
21452     strength: 0,
21453     // private
21454     _lastPwd: null,
21455     // private
21456     kCapitalLetter: 0,
21457     kSmallLetter: 1,
21458     kDigit: 2,
21459     kPunctuation: 3,
21460     
21461     insecure: false,
21462     // private
21463     initEvents: function ()
21464     {
21465         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21466
21467         if (this.el.is('input[type=password]') && Roo.isSafari) {
21468             this.el.on('keydown', this.SafariOnKeyDown, this);
21469         }
21470
21471         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21472     },
21473     // private
21474     onRender: function (ct, position)
21475     {
21476         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21477         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21478         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21479
21480         this.trigger.createChild({
21481                    cn: [
21482                     {
21483                     //id: 'PwdMeter',
21484                     tag: 'div',
21485                     cls: 'roo-password-meter-grey col-xs-12',
21486                     style: {
21487                         //width: 0,
21488                         //width: this.meterWidth + 'px'                                                
21489                         }
21490                     },
21491                     {                            
21492                          cls: 'roo-password-meter-text'                          
21493                     }
21494                 ]            
21495         });
21496
21497          
21498         if (this.hideTrigger) {
21499             this.trigger.setDisplayed(false);
21500         }
21501         this.setSize(this.width || '', this.height || '');
21502     },
21503     // private
21504     onDestroy: function ()
21505     {
21506         if (this.trigger) {
21507             this.trigger.removeAllListeners();
21508             this.trigger.remove();
21509         }
21510         if (this.wrap) {
21511             this.wrap.remove();
21512         }
21513         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21514     },
21515     // private
21516     checkStrength: function ()
21517     {
21518         var pwd = this.inputEl().getValue();
21519         if (pwd == this._lastPwd) {
21520             return;
21521         }
21522
21523         var strength;
21524         if (this.ClientSideStrongPassword(pwd)) {
21525             strength = 3;
21526         } else if (this.ClientSideMediumPassword(pwd)) {
21527             strength = 2;
21528         } else if (this.ClientSideWeakPassword(pwd)) {
21529             strength = 1;
21530         } else {
21531             strength = 0;
21532         }
21533         
21534         Roo.log('strength1: ' + strength);
21535         
21536         //var pm = this.trigger.child('div/div/div').dom;
21537         var pm = this.trigger.child('div/div');
21538         pm.removeClass(this.meterClass);
21539         pm.addClass(this.meterClass[strength]);
21540                 
21541         
21542         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21543                 
21544         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21545         
21546         this._lastPwd = pwd;
21547     },
21548     reset: function ()
21549     {
21550         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21551         
21552         this._lastPwd = '';
21553         
21554         var pm = this.trigger.child('div/div');
21555         pm.removeClass(this.meterClass);
21556         pm.addClass('roo-password-meter-grey');        
21557         
21558         
21559         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21560         
21561         pt.innerHTML = '';
21562         this.inputEl().dom.type='password';
21563     },
21564     // private
21565     validateValue: function (value)
21566     {
21567         
21568         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21569             return false;
21570         }
21571         if (value.length == 0) {
21572             if (this.allowBlank) {
21573                 this.clearInvalid();
21574                 return true;
21575             }
21576
21577             this.markInvalid(this.errors.PwdEmpty);
21578             this.errorMsg = this.errors.PwdEmpty;
21579             return false;
21580         }
21581         
21582         if(this.insecure){
21583             return true;
21584         }
21585         
21586         if ('[\x21-\x7e]*'.match(value)) {
21587             this.markInvalid(this.errors.PwdBadChar);
21588             this.errorMsg = this.errors.PwdBadChar;
21589             return false;
21590         }
21591         if (value.length < 6) {
21592             this.markInvalid(this.errors.PwdShort);
21593             this.errorMsg = this.errors.PwdShort;
21594             return false;
21595         }
21596         if (value.length > 16) {
21597             this.markInvalid(this.errors.PwdLong);
21598             this.errorMsg = this.errors.PwdLong;
21599             return false;
21600         }
21601         var strength;
21602         if (this.ClientSideStrongPassword(value)) {
21603             strength = 3;
21604         } else if (this.ClientSideMediumPassword(value)) {
21605             strength = 2;
21606         } else if (this.ClientSideWeakPassword(value)) {
21607             strength = 1;
21608         } else {
21609             strength = 0;
21610         }
21611
21612         
21613         if (strength < 2) {
21614             //this.markInvalid(this.errors.TooWeak);
21615             this.errorMsg = this.errors.TooWeak;
21616             //return false;
21617         }
21618         
21619         
21620         console.log('strength2: ' + strength);
21621         
21622         //var pm = this.trigger.child('div/div/div').dom;
21623         
21624         var pm = this.trigger.child('div/div');
21625         pm.removeClass(this.meterClass);
21626         pm.addClass(this.meterClass[strength]);
21627                 
21628         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21629                 
21630         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21631         
21632         this.errorMsg = ''; 
21633         return true;
21634     },
21635     // private
21636     CharacterSetChecks: function (type)
21637     {
21638         this.type = type;
21639         this.fResult = false;
21640     },
21641     // private
21642     isctype: function (character, type)
21643     {
21644         switch (type) {  
21645             case this.kCapitalLetter:
21646                 if (character >= 'A' && character <= 'Z') {
21647                     return true;
21648                 }
21649                 break;
21650             
21651             case this.kSmallLetter:
21652                 if (character >= 'a' && character <= 'z') {
21653                     return true;
21654                 }
21655                 break;
21656             
21657             case this.kDigit:
21658                 if (character >= '0' && character <= '9') {
21659                     return true;
21660                 }
21661                 break;
21662             
21663             case this.kPunctuation:
21664                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21665                     return true;
21666                 }
21667                 break;
21668             
21669             default:
21670                 return false;
21671         }
21672
21673     },
21674     // private
21675     IsLongEnough: function (pwd, size)
21676     {
21677         return !(pwd == null || isNaN(size) || pwd.length < size);
21678     },
21679     // private
21680     SpansEnoughCharacterSets: function (word, nb)
21681     {
21682         if (!this.IsLongEnough(word, nb))
21683         {
21684             return false;
21685         }
21686
21687         var characterSetChecks = new Array(
21688             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21689             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21690         );
21691         
21692         for (var index = 0; index < word.length; ++index) {
21693             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21694                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21695                     characterSetChecks[nCharSet].fResult = true;
21696                     break;
21697                 }
21698             }
21699         }
21700
21701         var nCharSets = 0;
21702         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21703             if (characterSetChecks[nCharSet].fResult) {
21704                 ++nCharSets;
21705             }
21706         }
21707
21708         if (nCharSets < nb) {
21709             return false;
21710         }
21711         return true;
21712     },
21713     // private
21714     ClientSideStrongPassword: function (pwd)
21715     {
21716         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21717     },
21718     // private
21719     ClientSideMediumPassword: function (pwd)
21720     {
21721         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21722     },
21723     // private
21724     ClientSideWeakPassword: function (pwd)
21725     {
21726         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21727     }
21728           
21729 })//<script type="text/javascript">
21730
21731 /*
21732  * Based  Ext JS Library 1.1.1
21733  * Copyright(c) 2006-2007, Ext JS, LLC.
21734  * LGPL
21735  *
21736  */
21737  
21738 /**
21739  * @class Roo.HtmlEditorCore
21740  * @extends Roo.Component
21741  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21742  *
21743  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21744  */
21745
21746 Roo.HtmlEditorCore = function(config){
21747     
21748     
21749     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21750     
21751     
21752     this.addEvents({
21753         /**
21754          * @event initialize
21755          * Fires when the editor is fully initialized (including the iframe)
21756          * @param {Roo.HtmlEditorCore} this
21757          */
21758         initialize: true,
21759         /**
21760          * @event activate
21761          * Fires when the editor is first receives the focus. Any insertion must wait
21762          * until after this event.
21763          * @param {Roo.HtmlEditorCore} this
21764          */
21765         activate: true,
21766          /**
21767          * @event beforesync
21768          * Fires before the textarea is updated with content from the editor iframe. Return false
21769          * to cancel the sync.
21770          * @param {Roo.HtmlEditorCore} this
21771          * @param {String} html
21772          */
21773         beforesync: true,
21774          /**
21775          * @event beforepush
21776          * Fires before the iframe editor is updated with content from the textarea. Return false
21777          * to cancel the push.
21778          * @param {Roo.HtmlEditorCore} this
21779          * @param {String} html
21780          */
21781         beforepush: true,
21782          /**
21783          * @event sync
21784          * Fires when the textarea is updated with content from the editor iframe.
21785          * @param {Roo.HtmlEditorCore} this
21786          * @param {String} html
21787          */
21788         sync: true,
21789          /**
21790          * @event push
21791          * Fires when the iframe editor is updated with content from the textarea.
21792          * @param {Roo.HtmlEditorCore} this
21793          * @param {String} html
21794          */
21795         push: true,
21796         
21797         /**
21798          * @event editorevent
21799          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21800          * @param {Roo.HtmlEditorCore} this
21801          */
21802         editorevent: true
21803         
21804     });
21805     
21806     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21807     
21808     // defaults : white / black...
21809     this.applyBlacklists();
21810     
21811     
21812     
21813 };
21814
21815
21816 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21817
21818
21819      /**
21820      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21821      */
21822     
21823     owner : false,
21824     
21825      /**
21826      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21827      *                        Roo.resizable.
21828      */
21829     resizable : false,
21830      /**
21831      * @cfg {Number} height (in pixels)
21832      */   
21833     height: 300,
21834    /**
21835      * @cfg {Number} width (in pixels)
21836      */   
21837     width: 500,
21838     
21839     /**
21840      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21841      * 
21842      */
21843     stylesheets: false,
21844     
21845     // id of frame..
21846     frameId: false,
21847     
21848     // private properties
21849     validationEvent : false,
21850     deferHeight: true,
21851     initialized : false,
21852     activated : false,
21853     sourceEditMode : false,
21854     onFocus : Roo.emptyFn,
21855     iframePad:3,
21856     hideMode:'offsets',
21857     
21858     clearUp: true,
21859     
21860     // blacklist + whitelisted elements..
21861     black: false,
21862     white: false,
21863      
21864     bodyCls : '',
21865
21866     /**
21867      * Protected method that will not generally be called directly. It
21868      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21869      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21870      */
21871     getDocMarkup : function(){
21872         // body styles..
21873         var st = '';
21874         
21875         // inherit styels from page...?? 
21876         if (this.stylesheets === false) {
21877             
21878             Roo.get(document.head).select('style').each(function(node) {
21879                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21880             });
21881             
21882             Roo.get(document.head).select('link').each(function(node) { 
21883                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21884             });
21885             
21886         } else if (!this.stylesheets.length) {
21887                 // simple..
21888                 st = '<style type="text/css">' +
21889                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21890                    '</style>';
21891         } else { 
21892             st = '<style type="text/css">' +
21893                     this.stylesheets +
21894                 '</style>';
21895         }
21896         
21897         st +=  '<style type="text/css">' +
21898             'IMG { cursor: pointer } ' +
21899         '</style>';
21900
21901         var cls = 'roo-htmleditor-body';
21902         
21903         if(this.bodyCls.length){
21904             cls += ' ' + this.bodyCls;
21905         }
21906         
21907         return '<html><head>' + st  +
21908             //<style type="text/css">' +
21909             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21910             //'</style>' +
21911             ' </head><body class="' +  cls + '"></body></html>';
21912     },
21913
21914     // private
21915     onRender : function(ct, position)
21916     {
21917         var _t = this;
21918         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21919         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21920         
21921         
21922         this.el.dom.style.border = '0 none';
21923         this.el.dom.setAttribute('tabIndex', -1);
21924         this.el.addClass('x-hidden hide');
21925         
21926         
21927         
21928         if(Roo.isIE){ // fix IE 1px bogus margin
21929             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21930         }
21931        
21932         
21933         this.frameId = Roo.id();
21934         
21935          
21936         
21937         var iframe = this.owner.wrap.createChild({
21938             tag: 'iframe',
21939             cls: 'form-control', // bootstrap..
21940             id: this.frameId,
21941             name: this.frameId,
21942             frameBorder : 'no',
21943             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21944         }, this.el
21945         );
21946         
21947         
21948         this.iframe = iframe.dom;
21949
21950          this.assignDocWin();
21951         
21952         this.doc.designMode = 'on';
21953        
21954         this.doc.open();
21955         this.doc.write(this.getDocMarkup());
21956         this.doc.close();
21957
21958         
21959         var task = { // must defer to wait for browser to be ready
21960             run : function(){
21961                 //console.log("run task?" + this.doc.readyState);
21962                 this.assignDocWin();
21963                 if(this.doc.body || this.doc.readyState == 'complete'){
21964                     try {
21965                         this.doc.designMode="on";
21966                     } catch (e) {
21967                         return;
21968                     }
21969                     Roo.TaskMgr.stop(task);
21970                     this.initEditor.defer(10, this);
21971                 }
21972             },
21973             interval : 10,
21974             duration: 10000,
21975             scope: this
21976         };
21977         Roo.TaskMgr.start(task);
21978
21979     },
21980
21981     // private
21982     onResize : function(w, h)
21983     {
21984          Roo.log('resize: ' +w + ',' + h );
21985         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21986         if(!this.iframe){
21987             return;
21988         }
21989         if(typeof w == 'number'){
21990             
21991             this.iframe.style.width = w + 'px';
21992         }
21993         if(typeof h == 'number'){
21994             
21995             this.iframe.style.height = h + 'px';
21996             if(this.doc){
21997                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21998             }
21999         }
22000         
22001     },
22002
22003     /**
22004      * Toggles the editor between standard and source edit mode.
22005      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22006      */
22007     toggleSourceEdit : function(sourceEditMode){
22008         
22009         this.sourceEditMode = sourceEditMode === true;
22010         
22011         if(this.sourceEditMode){
22012  
22013             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22014             
22015         }else{
22016             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22017             //this.iframe.className = '';
22018             this.deferFocus();
22019         }
22020         //this.setSize(this.owner.wrap.getSize());
22021         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22022     },
22023
22024     
22025   
22026
22027     /**
22028      * Protected method that will not generally be called directly. If you need/want
22029      * custom HTML cleanup, this is the method you should override.
22030      * @param {String} html The HTML to be cleaned
22031      * return {String} The cleaned HTML
22032      */
22033     cleanHtml : function(html){
22034         html = String(html);
22035         if(html.length > 5){
22036             if(Roo.isSafari){ // strip safari nonsense
22037                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22038             }
22039         }
22040         if(html == '&nbsp;'){
22041             html = '';
22042         }
22043         return html;
22044     },
22045
22046     /**
22047      * HTML Editor -> Textarea
22048      * Protected method that will not generally be called directly. Syncs the contents
22049      * of the editor iframe with the textarea.
22050      */
22051     syncValue : function(){
22052         if(this.initialized){
22053             var bd = (this.doc.body || this.doc.documentElement);
22054             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22055             var html = bd.innerHTML;
22056             if(Roo.isSafari){
22057                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22058                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22059                 if(m && m[1]){
22060                     html = '<div style="'+m[0]+'">' + html + '</div>';
22061                 }
22062             }
22063             html = this.cleanHtml(html);
22064             // fix up the special chars.. normaly like back quotes in word...
22065             // however we do not want to do this with chinese..
22066             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22067                 var cc = b.charCodeAt();
22068                 if (
22069                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22070                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22071                     (cc >= 0xf900 && cc < 0xfb00 )
22072                 ) {
22073                         return b;
22074                 }
22075                 return "&#"+cc+";" 
22076             });
22077             if(this.owner.fireEvent('beforesync', this, html) !== false){
22078                 this.el.dom.value = html;
22079                 this.owner.fireEvent('sync', this, html);
22080             }
22081         }
22082     },
22083
22084     /**
22085      * Protected method that will not generally be called directly. Pushes the value of the textarea
22086      * into the iframe editor.
22087      */
22088     pushValue : function(){
22089         if(this.initialized){
22090             var v = this.el.dom.value.trim();
22091             
22092 //            if(v.length < 1){
22093 //                v = '&#160;';
22094 //            }
22095             
22096             if(this.owner.fireEvent('beforepush', this, v) !== false){
22097                 var d = (this.doc.body || this.doc.documentElement);
22098                 d.innerHTML = v;
22099                 this.cleanUpPaste();
22100                 this.el.dom.value = d.innerHTML;
22101                 this.owner.fireEvent('push', this, v);
22102             }
22103         }
22104     },
22105
22106     // private
22107     deferFocus : function(){
22108         this.focus.defer(10, this);
22109     },
22110
22111     // doc'ed in Field
22112     focus : function(){
22113         if(this.win && !this.sourceEditMode){
22114             this.win.focus();
22115         }else{
22116             this.el.focus();
22117         }
22118     },
22119     
22120     assignDocWin: function()
22121     {
22122         var iframe = this.iframe;
22123         
22124          if(Roo.isIE){
22125             this.doc = iframe.contentWindow.document;
22126             this.win = iframe.contentWindow;
22127         } else {
22128 //            if (!Roo.get(this.frameId)) {
22129 //                return;
22130 //            }
22131 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22132 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22133             
22134             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22135                 return;
22136             }
22137             
22138             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22139             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22140         }
22141     },
22142     
22143     // private
22144     initEditor : function(){
22145         //console.log("INIT EDITOR");
22146         this.assignDocWin();
22147         
22148         
22149         
22150         this.doc.designMode="on";
22151         this.doc.open();
22152         this.doc.write(this.getDocMarkup());
22153         this.doc.close();
22154         
22155         var dbody = (this.doc.body || this.doc.documentElement);
22156         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22157         // this copies styles from the containing element into thsi one..
22158         // not sure why we need all of this..
22159         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22160         
22161         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22162         //ss['background-attachment'] = 'fixed'; // w3c
22163         dbody.bgProperties = 'fixed'; // ie
22164         //Roo.DomHelper.applyStyles(dbody, ss);
22165         Roo.EventManager.on(this.doc, {
22166             //'mousedown': this.onEditorEvent,
22167             'mouseup': this.onEditorEvent,
22168             'dblclick': this.onEditorEvent,
22169             'click': this.onEditorEvent,
22170             'keyup': this.onEditorEvent,
22171             buffer:100,
22172             scope: this
22173         });
22174         if(Roo.isGecko){
22175             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22176         }
22177         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22178             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22179         }
22180         this.initialized = true;
22181
22182         this.owner.fireEvent('initialize', this);
22183         this.pushValue();
22184     },
22185
22186     // private
22187     onDestroy : function(){
22188         
22189         
22190         
22191         if(this.rendered){
22192             
22193             //for (var i =0; i < this.toolbars.length;i++) {
22194             //    // fixme - ask toolbars for heights?
22195             //    this.toolbars[i].onDestroy();
22196            // }
22197             
22198             //this.wrap.dom.innerHTML = '';
22199             //this.wrap.remove();
22200         }
22201     },
22202
22203     // private
22204     onFirstFocus : function(){
22205         
22206         this.assignDocWin();
22207         
22208         
22209         this.activated = true;
22210          
22211     
22212         if(Roo.isGecko){ // prevent silly gecko errors
22213             this.win.focus();
22214             var s = this.win.getSelection();
22215             if(!s.focusNode || s.focusNode.nodeType != 3){
22216                 var r = s.getRangeAt(0);
22217                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22218                 r.collapse(true);
22219                 this.deferFocus();
22220             }
22221             try{
22222                 this.execCmd('useCSS', true);
22223                 this.execCmd('styleWithCSS', false);
22224             }catch(e){}
22225         }
22226         this.owner.fireEvent('activate', this);
22227     },
22228
22229     // private
22230     adjustFont: function(btn){
22231         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22232         //if(Roo.isSafari){ // safari
22233         //    adjust *= 2;
22234        // }
22235         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22236         if(Roo.isSafari){ // safari
22237             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22238             v =  (v < 10) ? 10 : v;
22239             v =  (v > 48) ? 48 : v;
22240             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22241             
22242         }
22243         
22244         
22245         v = Math.max(1, v+adjust);
22246         
22247         this.execCmd('FontSize', v  );
22248     },
22249
22250     onEditorEvent : function(e)
22251     {
22252         this.owner.fireEvent('editorevent', this, e);
22253       //  this.updateToolbar();
22254         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22255     },
22256
22257     insertTag : function(tg)
22258     {
22259         // could be a bit smarter... -> wrap the current selected tRoo..
22260         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22261             
22262             range = this.createRange(this.getSelection());
22263             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22264             wrappingNode.appendChild(range.extractContents());
22265             range.insertNode(wrappingNode);
22266
22267             return;
22268             
22269             
22270             
22271         }
22272         this.execCmd("formatblock",   tg);
22273         
22274     },
22275     
22276     insertText : function(txt)
22277     {
22278         
22279         
22280         var range = this.createRange();
22281         range.deleteContents();
22282                //alert(Sender.getAttribute('label'));
22283                
22284         range.insertNode(this.doc.createTextNode(txt));
22285     } ,
22286     
22287      
22288
22289     /**
22290      * Executes a Midas editor command on the editor document and performs necessary focus and
22291      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22292      * @param {String} cmd The Midas command
22293      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22294      */
22295     relayCmd : function(cmd, value){
22296         this.win.focus();
22297         this.execCmd(cmd, value);
22298         this.owner.fireEvent('editorevent', this);
22299         //this.updateToolbar();
22300         this.owner.deferFocus();
22301     },
22302
22303     /**
22304      * Executes a Midas editor command directly on the editor document.
22305      * For visual commands, you should use {@link #relayCmd} instead.
22306      * <b>This should only be called after the editor is initialized.</b>
22307      * @param {String} cmd The Midas command
22308      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22309      */
22310     execCmd : function(cmd, value){
22311         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22312         this.syncValue();
22313     },
22314  
22315  
22316    
22317     /**
22318      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22319      * to insert tRoo.
22320      * @param {String} text | dom node.. 
22321      */
22322     insertAtCursor : function(text)
22323     {
22324         
22325         if(!this.activated){
22326             return;
22327         }
22328         /*
22329         if(Roo.isIE){
22330             this.win.focus();
22331             var r = this.doc.selection.createRange();
22332             if(r){
22333                 r.collapse(true);
22334                 r.pasteHTML(text);
22335                 this.syncValue();
22336                 this.deferFocus();
22337             
22338             }
22339             return;
22340         }
22341         */
22342         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22343             this.win.focus();
22344             
22345             
22346             // from jquery ui (MIT licenced)
22347             var range, node;
22348             var win = this.win;
22349             
22350             if (win.getSelection && win.getSelection().getRangeAt) {
22351                 range = win.getSelection().getRangeAt(0);
22352                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22353                 range.insertNode(node);
22354             } else if (win.document.selection && win.document.selection.createRange) {
22355                 // no firefox support
22356                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22357                 win.document.selection.createRange().pasteHTML(txt);
22358             } else {
22359                 // no firefox support
22360                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22361                 this.execCmd('InsertHTML', txt);
22362             } 
22363             
22364             this.syncValue();
22365             
22366             this.deferFocus();
22367         }
22368     },
22369  // private
22370     mozKeyPress : function(e){
22371         if(e.ctrlKey){
22372             var c = e.getCharCode(), cmd;
22373           
22374             if(c > 0){
22375                 c = String.fromCharCode(c).toLowerCase();
22376                 switch(c){
22377                     case 'b':
22378                         cmd = 'bold';
22379                         break;
22380                     case 'i':
22381                         cmd = 'italic';
22382                         break;
22383                     
22384                     case 'u':
22385                         cmd = 'underline';
22386                         break;
22387                     
22388                     case 'v':
22389                         this.cleanUpPaste.defer(100, this);
22390                         return;
22391                         
22392                 }
22393                 if(cmd){
22394                     this.win.focus();
22395                     this.execCmd(cmd);
22396                     this.deferFocus();
22397                     e.preventDefault();
22398                 }
22399                 
22400             }
22401         }
22402     },
22403
22404     // private
22405     fixKeys : function(){ // load time branching for fastest keydown performance
22406         if(Roo.isIE){
22407             return function(e){
22408                 var k = e.getKey(), r;
22409                 if(k == e.TAB){
22410                     e.stopEvent();
22411                     r = this.doc.selection.createRange();
22412                     if(r){
22413                         r.collapse(true);
22414                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22415                         this.deferFocus();
22416                     }
22417                     return;
22418                 }
22419                 
22420                 if(k == e.ENTER){
22421                     r = this.doc.selection.createRange();
22422                     if(r){
22423                         var target = r.parentElement();
22424                         if(!target || target.tagName.toLowerCase() != 'li'){
22425                             e.stopEvent();
22426                             r.pasteHTML('<br />');
22427                             r.collapse(false);
22428                             r.select();
22429                         }
22430                     }
22431                 }
22432                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22433                     this.cleanUpPaste.defer(100, this);
22434                     return;
22435                 }
22436                 
22437                 
22438             };
22439         }else if(Roo.isOpera){
22440             return function(e){
22441                 var k = e.getKey();
22442                 if(k == e.TAB){
22443                     e.stopEvent();
22444                     this.win.focus();
22445                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22446                     this.deferFocus();
22447                 }
22448                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22449                     this.cleanUpPaste.defer(100, this);
22450                     return;
22451                 }
22452                 
22453             };
22454         }else if(Roo.isSafari){
22455             return function(e){
22456                 var k = e.getKey();
22457                 
22458                 if(k == e.TAB){
22459                     e.stopEvent();
22460                     this.execCmd('InsertText','\t');
22461                     this.deferFocus();
22462                     return;
22463                 }
22464                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22465                     this.cleanUpPaste.defer(100, this);
22466                     return;
22467                 }
22468                 
22469              };
22470         }
22471     }(),
22472     
22473     getAllAncestors: function()
22474     {
22475         var p = this.getSelectedNode();
22476         var a = [];
22477         if (!p) {
22478             a.push(p); // push blank onto stack..
22479             p = this.getParentElement();
22480         }
22481         
22482         
22483         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22484             a.push(p);
22485             p = p.parentNode;
22486         }
22487         a.push(this.doc.body);
22488         return a;
22489     },
22490     lastSel : false,
22491     lastSelNode : false,
22492     
22493     
22494     getSelection : function() 
22495     {
22496         this.assignDocWin();
22497         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22498     },
22499     
22500     getSelectedNode: function() 
22501     {
22502         // this may only work on Gecko!!!
22503         
22504         // should we cache this!!!!
22505         
22506         
22507         
22508          
22509         var range = this.createRange(this.getSelection()).cloneRange();
22510         
22511         if (Roo.isIE) {
22512             var parent = range.parentElement();
22513             while (true) {
22514                 var testRange = range.duplicate();
22515                 testRange.moveToElementText(parent);
22516                 if (testRange.inRange(range)) {
22517                     break;
22518                 }
22519                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22520                     break;
22521                 }
22522                 parent = parent.parentElement;
22523             }
22524             return parent;
22525         }
22526         
22527         // is ancestor a text element.
22528         var ac =  range.commonAncestorContainer;
22529         if (ac.nodeType == 3) {
22530             ac = ac.parentNode;
22531         }
22532         
22533         var ar = ac.childNodes;
22534          
22535         var nodes = [];
22536         var other_nodes = [];
22537         var has_other_nodes = false;
22538         for (var i=0;i<ar.length;i++) {
22539             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22540                 continue;
22541             }
22542             // fullly contained node.
22543             
22544             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22545                 nodes.push(ar[i]);
22546                 continue;
22547             }
22548             
22549             // probably selected..
22550             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22551                 other_nodes.push(ar[i]);
22552                 continue;
22553             }
22554             // outer..
22555             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22556                 continue;
22557             }
22558             
22559             
22560             has_other_nodes = true;
22561         }
22562         if (!nodes.length && other_nodes.length) {
22563             nodes= other_nodes;
22564         }
22565         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22566             return false;
22567         }
22568         
22569         return nodes[0];
22570     },
22571     createRange: function(sel)
22572     {
22573         // this has strange effects when using with 
22574         // top toolbar - not sure if it's a great idea.
22575         //this.editor.contentWindow.focus();
22576         if (typeof sel != "undefined") {
22577             try {
22578                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22579             } catch(e) {
22580                 return this.doc.createRange();
22581             }
22582         } else {
22583             return this.doc.createRange();
22584         }
22585     },
22586     getParentElement: function()
22587     {
22588         
22589         this.assignDocWin();
22590         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22591         
22592         var range = this.createRange(sel);
22593          
22594         try {
22595             var p = range.commonAncestorContainer;
22596             while (p.nodeType == 3) { // text node
22597                 p = p.parentNode;
22598             }
22599             return p;
22600         } catch (e) {
22601             return null;
22602         }
22603     
22604     },
22605     /***
22606      *
22607      * Range intersection.. the hard stuff...
22608      *  '-1' = before
22609      *  '0' = hits..
22610      *  '1' = after.
22611      *         [ -- selected range --- ]
22612      *   [fail]                        [fail]
22613      *
22614      *    basically..
22615      *      if end is before start or  hits it. fail.
22616      *      if start is after end or hits it fail.
22617      *
22618      *   if either hits (but other is outside. - then it's not 
22619      *   
22620      *    
22621      **/
22622     
22623     
22624     // @see http://www.thismuchiknow.co.uk/?p=64.
22625     rangeIntersectsNode : function(range, node)
22626     {
22627         var nodeRange = node.ownerDocument.createRange();
22628         try {
22629             nodeRange.selectNode(node);
22630         } catch (e) {
22631             nodeRange.selectNodeContents(node);
22632         }
22633     
22634         var rangeStartRange = range.cloneRange();
22635         rangeStartRange.collapse(true);
22636     
22637         var rangeEndRange = range.cloneRange();
22638         rangeEndRange.collapse(false);
22639     
22640         var nodeStartRange = nodeRange.cloneRange();
22641         nodeStartRange.collapse(true);
22642     
22643         var nodeEndRange = nodeRange.cloneRange();
22644         nodeEndRange.collapse(false);
22645     
22646         return rangeStartRange.compareBoundaryPoints(
22647                  Range.START_TO_START, nodeEndRange) == -1 &&
22648                rangeEndRange.compareBoundaryPoints(
22649                  Range.START_TO_START, nodeStartRange) == 1;
22650         
22651          
22652     },
22653     rangeCompareNode : function(range, node)
22654     {
22655         var nodeRange = node.ownerDocument.createRange();
22656         try {
22657             nodeRange.selectNode(node);
22658         } catch (e) {
22659             nodeRange.selectNodeContents(node);
22660         }
22661         
22662         
22663         range.collapse(true);
22664     
22665         nodeRange.collapse(true);
22666      
22667         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22668         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22669          
22670         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22671         
22672         var nodeIsBefore   =  ss == 1;
22673         var nodeIsAfter    = ee == -1;
22674         
22675         if (nodeIsBefore && nodeIsAfter) {
22676             return 0; // outer
22677         }
22678         if (!nodeIsBefore && nodeIsAfter) {
22679             return 1; //right trailed.
22680         }
22681         
22682         if (nodeIsBefore && !nodeIsAfter) {
22683             return 2;  // left trailed.
22684         }
22685         // fully contined.
22686         return 3;
22687     },
22688
22689     // private? - in a new class?
22690     cleanUpPaste :  function()
22691     {
22692         // cleans up the whole document..
22693         Roo.log('cleanuppaste');
22694         
22695         this.cleanUpChildren(this.doc.body);
22696         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22697         if (clean != this.doc.body.innerHTML) {
22698             this.doc.body.innerHTML = clean;
22699         }
22700         
22701     },
22702     
22703     cleanWordChars : function(input) {// change the chars to hex code
22704         var he = Roo.HtmlEditorCore;
22705         
22706         var output = input;
22707         Roo.each(he.swapCodes, function(sw) { 
22708             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22709             
22710             output = output.replace(swapper, sw[1]);
22711         });
22712         
22713         return output;
22714     },
22715     
22716     
22717     cleanUpChildren : function (n)
22718     {
22719         if (!n.childNodes.length) {
22720             return;
22721         }
22722         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22723            this.cleanUpChild(n.childNodes[i]);
22724         }
22725     },
22726     
22727     
22728         
22729     
22730     cleanUpChild : function (node)
22731     {
22732         var ed = this;
22733         //console.log(node);
22734         if (node.nodeName == "#text") {
22735             // clean up silly Windows -- stuff?
22736             return; 
22737         }
22738         if (node.nodeName == "#comment") {
22739             node.parentNode.removeChild(node);
22740             // clean up silly Windows -- stuff?
22741             return; 
22742         }
22743         var lcname = node.tagName.toLowerCase();
22744         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22745         // whitelist of tags..
22746         
22747         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22748             // remove node.
22749             node.parentNode.removeChild(node);
22750             return;
22751             
22752         }
22753         
22754         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22755         
22756         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22757         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22758         
22759         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22760         //    remove_keep_children = true;
22761         //}
22762         
22763         if (remove_keep_children) {
22764             this.cleanUpChildren(node);
22765             // inserts everything just before this node...
22766             while (node.childNodes.length) {
22767                 var cn = node.childNodes[0];
22768                 node.removeChild(cn);
22769                 node.parentNode.insertBefore(cn, node);
22770             }
22771             node.parentNode.removeChild(node);
22772             return;
22773         }
22774         
22775         if (!node.attributes || !node.attributes.length) {
22776             this.cleanUpChildren(node);
22777             return;
22778         }
22779         
22780         function cleanAttr(n,v)
22781         {
22782             
22783             if (v.match(/^\./) || v.match(/^\//)) {
22784                 return;
22785             }
22786             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22787                 return;
22788             }
22789             if (v.match(/^#/)) {
22790                 return;
22791             }
22792 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22793             node.removeAttribute(n);
22794             
22795         }
22796         
22797         var cwhite = this.cwhite;
22798         var cblack = this.cblack;
22799             
22800         function cleanStyle(n,v)
22801         {
22802             if (v.match(/expression/)) { //XSS?? should we even bother..
22803                 node.removeAttribute(n);
22804                 return;
22805             }
22806             
22807             var parts = v.split(/;/);
22808             var clean = [];
22809             
22810             Roo.each(parts, function(p) {
22811                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22812                 if (!p.length) {
22813                     return true;
22814                 }
22815                 var l = p.split(':').shift().replace(/\s+/g,'');
22816                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22817                 
22818                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22819 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22820                     //node.removeAttribute(n);
22821                     return true;
22822                 }
22823                 //Roo.log()
22824                 // only allow 'c whitelisted system attributes'
22825                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22826 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22827                     //node.removeAttribute(n);
22828                     return true;
22829                 }
22830                 
22831                 
22832                  
22833                 
22834                 clean.push(p);
22835                 return true;
22836             });
22837             if (clean.length) { 
22838                 node.setAttribute(n, clean.join(';'));
22839             } else {
22840                 node.removeAttribute(n);
22841             }
22842             
22843         }
22844         
22845         
22846         for (var i = node.attributes.length-1; i > -1 ; i--) {
22847             var a = node.attributes[i];
22848             //console.log(a);
22849             
22850             if (a.name.toLowerCase().substr(0,2)=='on')  {
22851                 node.removeAttribute(a.name);
22852                 continue;
22853             }
22854             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22855                 node.removeAttribute(a.name);
22856                 continue;
22857             }
22858             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22859                 cleanAttr(a.name,a.value); // fixme..
22860                 continue;
22861             }
22862             if (a.name == 'style') {
22863                 cleanStyle(a.name,a.value);
22864                 continue;
22865             }
22866             /// clean up MS crap..
22867             // tecnically this should be a list of valid class'es..
22868             
22869             
22870             if (a.name == 'class') {
22871                 if (a.value.match(/^Mso/)) {
22872                     node.className = '';
22873                 }
22874                 
22875                 if (a.value.match(/^body$/)) {
22876                     node.className = '';
22877                 }
22878                 continue;
22879             }
22880             
22881             // style cleanup!?
22882             // class cleanup?
22883             
22884         }
22885         
22886         
22887         this.cleanUpChildren(node);
22888         
22889         
22890     },
22891     
22892     /**
22893      * Clean up MS wordisms...
22894      */
22895     cleanWord : function(node)
22896     {
22897         
22898         
22899         if (!node) {
22900             this.cleanWord(this.doc.body);
22901             return;
22902         }
22903         if (node.nodeName == "#text") {
22904             // clean up silly Windows -- stuff?
22905             return; 
22906         }
22907         if (node.nodeName == "#comment") {
22908             node.parentNode.removeChild(node);
22909             // clean up silly Windows -- stuff?
22910             return; 
22911         }
22912         
22913         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22914             node.parentNode.removeChild(node);
22915             return;
22916         }
22917         
22918         // remove - but keep children..
22919         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22920             while (node.childNodes.length) {
22921                 var cn = node.childNodes[0];
22922                 node.removeChild(cn);
22923                 node.parentNode.insertBefore(cn, node);
22924             }
22925             node.parentNode.removeChild(node);
22926             this.iterateChildren(node, this.cleanWord);
22927             return;
22928         }
22929         // clean styles
22930         if (node.className.length) {
22931             
22932             var cn = node.className.split(/\W+/);
22933             var cna = [];
22934             Roo.each(cn, function(cls) {
22935                 if (cls.match(/Mso[a-zA-Z]+/)) {
22936                     return;
22937                 }
22938                 cna.push(cls);
22939             });
22940             node.className = cna.length ? cna.join(' ') : '';
22941             if (!cna.length) {
22942                 node.removeAttribute("class");
22943             }
22944         }
22945         
22946         if (node.hasAttribute("lang")) {
22947             node.removeAttribute("lang");
22948         }
22949         
22950         if (node.hasAttribute("style")) {
22951             
22952             var styles = node.getAttribute("style").split(";");
22953             var nstyle = [];
22954             Roo.each(styles, function(s) {
22955                 if (!s.match(/:/)) {
22956                     return;
22957                 }
22958                 var kv = s.split(":");
22959                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22960                     return;
22961                 }
22962                 // what ever is left... we allow.
22963                 nstyle.push(s);
22964             });
22965             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22966             if (!nstyle.length) {
22967                 node.removeAttribute('style');
22968             }
22969         }
22970         this.iterateChildren(node, this.cleanWord);
22971         
22972         
22973         
22974     },
22975     /**
22976      * iterateChildren of a Node, calling fn each time, using this as the scole..
22977      * @param {DomNode} node node to iterate children of.
22978      * @param {Function} fn method of this class to call on each item.
22979      */
22980     iterateChildren : function(node, fn)
22981     {
22982         if (!node.childNodes.length) {
22983                 return;
22984         }
22985         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22986            fn.call(this, node.childNodes[i])
22987         }
22988     },
22989     
22990     
22991     /**
22992      * cleanTableWidths.
22993      *
22994      * Quite often pasting from word etc.. results in tables with column and widths.
22995      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22996      *
22997      */
22998     cleanTableWidths : function(node)
22999     {
23000          
23001          
23002         if (!node) {
23003             this.cleanTableWidths(this.doc.body);
23004             return;
23005         }
23006         
23007         // ignore list...
23008         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23009             return; 
23010         }
23011         Roo.log(node.tagName);
23012         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23013             this.iterateChildren(node, this.cleanTableWidths);
23014             return;
23015         }
23016         if (node.hasAttribute('width')) {
23017             node.removeAttribute('width');
23018         }
23019         
23020          
23021         if (node.hasAttribute("style")) {
23022             // pretty basic...
23023             
23024             var styles = node.getAttribute("style").split(";");
23025             var nstyle = [];
23026             Roo.each(styles, function(s) {
23027                 if (!s.match(/:/)) {
23028                     return;
23029                 }
23030                 var kv = s.split(":");
23031                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23032                     return;
23033                 }
23034                 // what ever is left... we allow.
23035                 nstyle.push(s);
23036             });
23037             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23038             if (!nstyle.length) {
23039                 node.removeAttribute('style');
23040             }
23041         }
23042         
23043         this.iterateChildren(node, this.cleanTableWidths);
23044         
23045         
23046     },
23047     
23048     
23049     
23050     
23051     domToHTML : function(currentElement, depth, nopadtext) {
23052         
23053         depth = depth || 0;
23054         nopadtext = nopadtext || false;
23055     
23056         if (!currentElement) {
23057             return this.domToHTML(this.doc.body);
23058         }
23059         
23060         //Roo.log(currentElement);
23061         var j;
23062         var allText = false;
23063         var nodeName = currentElement.nodeName;
23064         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23065         
23066         if  (nodeName == '#text') {
23067             
23068             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23069         }
23070         
23071         
23072         var ret = '';
23073         if (nodeName != 'BODY') {
23074              
23075             var i = 0;
23076             // Prints the node tagName, such as <A>, <IMG>, etc
23077             if (tagName) {
23078                 var attr = [];
23079                 for(i = 0; i < currentElement.attributes.length;i++) {
23080                     // quoting?
23081                     var aname = currentElement.attributes.item(i).name;
23082                     if (!currentElement.attributes.item(i).value.length) {
23083                         continue;
23084                     }
23085                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23086                 }
23087                 
23088                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23089             } 
23090             else {
23091                 
23092                 // eack
23093             }
23094         } else {
23095             tagName = false;
23096         }
23097         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23098             return ret;
23099         }
23100         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23101             nopadtext = true;
23102         }
23103         
23104         
23105         // Traverse the tree
23106         i = 0;
23107         var currentElementChild = currentElement.childNodes.item(i);
23108         var allText = true;
23109         var innerHTML  = '';
23110         lastnode = '';
23111         while (currentElementChild) {
23112             // Formatting code (indent the tree so it looks nice on the screen)
23113             var nopad = nopadtext;
23114             if (lastnode == 'SPAN') {
23115                 nopad  = true;
23116             }
23117             // text
23118             if  (currentElementChild.nodeName == '#text') {
23119                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23120                 toadd = nopadtext ? toadd : toadd.trim();
23121                 if (!nopad && toadd.length > 80) {
23122                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23123                 }
23124                 innerHTML  += toadd;
23125                 
23126                 i++;
23127                 currentElementChild = currentElement.childNodes.item(i);
23128                 lastNode = '';
23129                 continue;
23130             }
23131             allText = false;
23132             
23133             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23134                 
23135             // Recursively traverse the tree structure of the child node
23136             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23137             lastnode = currentElementChild.nodeName;
23138             i++;
23139             currentElementChild=currentElement.childNodes.item(i);
23140         }
23141         
23142         ret += innerHTML;
23143         
23144         if (!allText) {
23145                 // The remaining code is mostly for formatting the tree
23146             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23147         }
23148         
23149         
23150         if (tagName) {
23151             ret+= "</"+tagName+">";
23152         }
23153         return ret;
23154         
23155     },
23156         
23157     applyBlacklists : function()
23158     {
23159         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23160         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23161         
23162         this.white = [];
23163         this.black = [];
23164         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23165             if (b.indexOf(tag) > -1) {
23166                 return;
23167             }
23168             this.white.push(tag);
23169             
23170         }, this);
23171         
23172         Roo.each(w, function(tag) {
23173             if (b.indexOf(tag) > -1) {
23174                 return;
23175             }
23176             if (this.white.indexOf(tag) > -1) {
23177                 return;
23178             }
23179             this.white.push(tag);
23180             
23181         }, this);
23182         
23183         
23184         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23185             if (w.indexOf(tag) > -1) {
23186                 return;
23187             }
23188             this.black.push(tag);
23189             
23190         }, this);
23191         
23192         Roo.each(b, function(tag) {
23193             if (w.indexOf(tag) > -1) {
23194                 return;
23195             }
23196             if (this.black.indexOf(tag) > -1) {
23197                 return;
23198             }
23199             this.black.push(tag);
23200             
23201         }, this);
23202         
23203         
23204         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23205         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23206         
23207         this.cwhite = [];
23208         this.cblack = [];
23209         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23210             if (b.indexOf(tag) > -1) {
23211                 return;
23212             }
23213             this.cwhite.push(tag);
23214             
23215         }, this);
23216         
23217         Roo.each(w, function(tag) {
23218             if (b.indexOf(tag) > -1) {
23219                 return;
23220             }
23221             if (this.cwhite.indexOf(tag) > -1) {
23222                 return;
23223             }
23224             this.cwhite.push(tag);
23225             
23226         }, this);
23227         
23228         
23229         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23230             if (w.indexOf(tag) > -1) {
23231                 return;
23232             }
23233             this.cblack.push(tag);
23234             
23235         }, this);
23236         
23237         Roo.each(b, function(tag) {
23238             if (w.indexOf(tag) > -1) {
23239                 return;
23240             }
23241             if (this.cblack.indexOf(tag) > -1) {
23242                 return;
23243             }
23244             this.cblack.push(tag);
23245             
23246         }, this);
23247     },
23248     
23249     setStylesheets : function(stylesheets)
23250     {
23251         if(typeof(stylesheets) == 'string'){
23252             Roo.get(this.iframe.contentDocument.head).createChild({
23253                 tag : 'link',
23254                 rel : 'stylesheet',
23255                 type : 'text/css',
23256                 href : stylesheets
23257             });
23258             
23259             return;
23260         }
23261         var _this = this;
23262      
23263         Roo.each(stylesheets, function(s) {
23264             if(!s.length){
23265                 return;
23266             }
23267             
23268             Roo.get(_this.iframe.contentDocument.head).createChild({
23269                 tag : 'link',
23270                 rel : 'stylesheet',
23271                 type : 'text/css',
23272                 href : s
23273             });
23274         });
23275
23276         
23277     },
23278     
23279     removeStylesheets : function()
23280     {
23281         var _this = this;
23282         
23283         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23284             s.remove();
23285         });
23286     },
23287     
23288     setStyle : function(style)
23289     {
23290         Roo.get(this.iframe.contentDocument.head).createChild({
23291             tag : 'style',
23292             type : 'text/css',
23293             html : style
23294         });
23295
23296         return;
23297     }
23298     
23299     // hide stuff that is not compatible
23300     /**
23301      * @event blur
23302      * @hide
23303      */
23304     /**
23305      * @event change
23306      * @hide
23307      */
23308     /**
23309      * @event focus
23310      * @hide
23311      */
23312     /**
23313      * @event specialkey
23314      * @hide
23315      */
23316     /**
23317      * @cfg {String} fieldClass @hide
23318      */
23319     /**
23320      * @cfg {String} focusClass @hide
23321      */
23322     /**
23323      * @cfg {String} autoCreate @hide
23324      */
23325     /**
23326      * @cfg {String} inputType @hide
23327      */
23328     /**
23329      * @cfg {String} invalidClass @hide
23330      */
23331     /**
23332      * @cfg {String} invalidText @hide
23333      */
23334     /**
23335      * @cfg {String} msgFx @hide
23336      */
23337     /**
23338      * @cfg {String} validateOnBlur @hide
23339      */
23340 });
23341
23342 Roo.HtmlEditorCore.white = [
23343         'area', 'br', 'img', 'input', 'hr', 'wbr',
23344         
23345        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23346        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23347        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23348        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23349        'table',   'ul',         'xmp', 
23350        
23351        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23352       'thead',   'tr', 
23353      
23354       'dir', 'menu', 'ol', 'ul', 'dl',
23355        
23356       'embed',  'object'
23357 ];
23358
23359
23360 Roo.HtmlEditorCore.black = [
23361     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23362         'applet', // 
23363         'base',   'basefont', 'bgsound', 'blink',  'body', 
23364         'frame',  'frameset', 'head',    'html',   'ilayer', 
23365         'iframe', 'layer',  'link',     'meta',    'object',   
23366         'script', 'style' ,'title',  'xml' // clean later..
23367 ];
23368 Roo.HtmlEditorCore.clean = [
23369     'script', 'style', 'title', 'xml'
23370 ];
23371 Roo.HtmlEditorCore.remove = [
23372     'font'
23373 ];
23374 // attributes..
23375
23376 Roo.HtmlEditorCore.ablack = [
23377     'on'
23378 ];
23379     
23380 Roo.HtmlEditorCore.aclean = [ 
23381     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23382 ];
23383
23384 // protocols..
23385 Roo.HtmlEditorCore.pwhite= [
23386         'http',  'https',  'mailto'
23387 ];
23388
23389 // white listed style attributes.
23390 Roo.HtmlEditorCore.cwhite= [
23391       //  'text-align', /// default is to allow most things..
23392       
23393          
23394 //        'font-size'//??
23395 ];
23396
23397 // black listed style attributes.
23398 Roo.HtmlEditorCore.cblack= [
23399       //  'font-size' -- this can be set by the project 
23400 ];
23401
23402
23403 Roo.HtmlEditorCore.swapCodes   =[ 
23404     [    8211, "--" ], 
23405     [    8212, "--" ], 
23406     [    8216,  "'" ],  
23407     [    8217, "'" ],  
23408     [    8220, '"' ],  
23409     [    8221, '"' ],  
23410     [    8226, "*" ],  
23411     [    8230, "..." ]
23412 ]; 
23413
23414     /*
23415  * - LGPL
23416  *
23417  * HtmlEditor
23418  * 
23419  */
23420
23421 /**
23422  * @class Roo.bootstrap.HtmlEditor
23423  * @extends Roo.bootstrap.TextArea
23424  * Bootstrap HtmlEditor class
23425
23426  * @constructor
23427  * Create a new HtmlEditor
23428  * @param {Object} config The config object
23429  */
23430
23431 Roo.bootstrap.HtmlEditor = function(config){
23432     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23433     if (!this.toolbars) {
23434         this.toolbars = [];
23435     }
23436     
23437     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23438     this.addEvents({
23439             /**
23440              * @event initialize
23441              * Fires when the editor is fully initialized (including the iframe)
23442              * @param {HtmlEditor} this
23443              */
23444             initialize: true,
23445             /**
23446              * @event activate
23447              * Fires when the editor is first receives the focus. Any insertion must wait
23448              * until after this event.
23449              * @param {HtmlEditor} this
23450              */
23451             activate: true,
23452              /**
23453              * @event beforesync
23454              * Fires before the textarea is updated with content from the editor iframe. Return false
23455              * to cancel the sync.
23456              * @param {HtmlEditor} this
23457              * @param {String} html
23458              */
23459             beforesync: true,
23460              /**
23461              * @event beforepush
23462              * Fires before the iframe editor is updated with content from the textarea. Return false
23463              * to cancel the push.
23464              * @param {HtmlEditor} this
23465              * @param {String} html
23466              */
23467             beforepush: true,
23468              /**
23469              * @event sync
23470              * Fires when the textarea is updated with content from the editor iframe.
23471              * @param {HtmlEditor} this
23472              * @param {String} html
23473              */
23474             sync: true,
23475              /**
23476              * @event push
23477              * Fires when the iframe editor is updated with content from the textarea.
23478              * @param {HtmlEditor} this
23479              * @param {String} html
23480              */
23481             push: true,
23482              /**
23483              * @event editmodechange
23484              * Fires when the editor switches edit modes
23485              * @param {HtmlEditor} this
23486              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23487              */
23488             editmodechange: true,
23489             /**
23490              * @event editorevent
23491              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23492              * @param {HtmlEditor} this
23493              */
23494             editorevent: true,
23495             /**
23496              * @event firstfocus
23497              * Fires when on first focus - needed by toolbars..
23498              * @param {HtmlEditor} this
23499              */
23500             firstfocus: true,
23501             /**
23502              * @event autosave
23503              * Auto save the htmlEditor value as a file into Events
23504              * @param {HtmlEditor} this
23505              */
23506             autosave: true,
23507             /**
23508              * @event savedpreview
23509              * preview the saved version of htmlEditor
23510              * @param {HtmlEditor} this
23511              */
23512             savedpreview: true
23513         });
23514 };
23515
23516
23517 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23518     
23519     
23520       /**
23521      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23522      */
23523     toolbars : false,
23524     
23525      /**
23526     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23527     */
23528     btns : [],
23529    
23530      /**
23531      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23532      *                        Roo.resizable.
23533      */
23534     resizable : false,
23535      /**
23536      * @cfg {Number} height (in pixels)
23537      */   
23538     height: 300,
23539    /**
23540      * @cfg {Number} width (in pixels)
23541      */   
23542     width: false,
23543     
23544     /**
23545      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23546      * 
23547      */
23548     stylesheets: false,
23549     
23550     // id of frame..
23551     frameId: false,
23552     
23553     // private properties
23554     validationEvent : false,
23555     deferHeight: true,
23556     initialized : false,
23557     activated : false,
23558     
23559     onFocus : Roo.emptyFn,
23560     iframePad:3,
23561     hideMode:'offsets',
23562     
23563     tbContainer : false,
23564     
23565     bodyCls : '',
23566     
23567     toolbarContainer :function() {
23568         return this.wrap.select('.x-html-editor-tb',true).first();
23569     },
23570
23571     /**
23572      * Protected method that will not generally be called directly. It
23573      * is called when the editor creates its toolbar. Override this method if you need to
23574      * add custom toolbar buttons.
23575      * @param {HtmlEditor} editor
23576      */
23577     createToolbar : function(){
23578         Roo.log('renewing');
23579         Roo.log("create toolbars");
23580         
23581         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23582         this.toolbars[0].render(this.toolbarContainer());
23583         
23584         return;
23585         
23586 //        if (!editor.toolbars || !editor.toolbars.length) {
23587 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23588 //        }
23589 //        
23590 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23591 //            editor.toolbars[i] = Roo.factory(
23592 //                    typeof(editor.toolbars[i]) == 'string' ?
23593 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23594 //                Roo.bootstrap.HtmlEditor);
23595 //            editor.toolbars[i].init(editor);
23596 //        }
23597     },
23598
23599      
23600     // private
23601     onRender : function(ct, position)
23602     {
23603        // Roo.log("Call onRender: " + this.xtype);
23604         var _t = this;
23605         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23606       
23607         this.wrap = this.inputEl().wrap({
23608             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23609         });
23610         
23611         this.editorcore.onRender(ct, position);
23612          
23613         if (this.resizable) {
23614             this.resizeEl = new Roo.Resizable(this.wrap, {
23615                 pinned : true,
23616                 wrap: true,
23617                 dynamic : true,
23618                 minHeight : this.height,
23619                 height: this.height,
23620                 handles : this.resizable,
23621                 width: this.width,
23622                 listeners : {
23623                     resize : function(r, w, h) {
23624                         _t.onResize(w,h); // -something
23625                     }
23626                 }
23627             });
23628             
23629         }
23630         this.createToolbar(this);
23631        
23632         
23633         if(!this.width && this.resizable){
23634             this.setSize(this.wrap.getSize());
23635         }
23636         if (this.resizeEl) {
23637             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23638             // should trigger onReize..
23639         }
23640         
23641     },
23642
23643     // private
23644     onResize : function(w, h)
23645     {
23646         Roo.log('resize: ' +w + ',' + h );
23647         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23648         var ew = false;
23649         var eh = false;
23650         
23651         if(this.inputEl() ){
23652             if(typeof w == 'number'){
23653                 var aw = w - this.wrap.getFrameWidth('lr');
23654                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23655                 ew = aw;
23656             }
23657             if(typeof h == 'number'){
23658                  var tbh = -11;  // fixme it needs to tool bar size!
23659                 for (var i =0; i < this.toolbars.length;i++) {
23660                     // fixme - ask toolbars for heights?
23661                     tbh += this.toolbars[i].el.getHeight();
23662                     //if (this.toolbars[i].footer) {
23663                     //    tbh += this.toolbars[i].footer.el.getHeight();
23664                     //}
23665                 }
23666               
23667                 
23668                 
23669                 
23670                 
23671                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23672                 ah -= 5; // knock a few pixes off for look..
23673                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23674                 var eh = ah;
23675             }
23676         }
23677         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23678         this.editorcore.onResize(ew,eh);
23679         
23680     },
23681
23682     /**
23683      * Toggles the editor between standard and source edit mode.
23684      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23685      */
23686     toggleSourceEdit : function(sourceEditMode)
23687     {
23688         this.editorcore.toggleSourceEdit(sourceEditMode);
23689         
23690         if(this.editorcore.sourceEditMode){
23691             Roo.log('editor - showing textarea');
23692             
23693 //            Roo.log('in');
23694 //            Roo.log(this.syncValue());
23695             this.syncValue();
23696             this.inputEl().removeClass(['hide', 'x-hidden']);
23697             this.inputEl().dom.removeAttribute('tabIndex');
23698             this.inputEl().focus();
23699         }else{
23700             Roo.log('editor - hiding textarea');
23701 //            Roo.log('out')
23702 //            Roo.log(this.pushValue()); 
23703             this.pushValue();
23704             
23705             this.inputEl().addClass(['hide', 'x-hidden']);
23706             this.inputEl().dom.setAttribute('tabIndex', -1);
23707             //this.deferFocus();
23708         }
23709          
23710         if(this.resizable){
23711             this.setSize(this.wrap.getSize());
23712         }
23713         
23714         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23715     },
23716  
23717     // private (for BoxComponent)
23718     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23719
23720     // private (for BoxComponent)
23721     getResizeEl : function(){
23722         return this.wrap;
23723     },
23724
23725     // private (for BoxComponent)
23726     getPositionEl : function(){
23727         return this.wrap;
23728     },
23729
23730     // private
23731     initEvents : function(){
23732         this.originalValue = this.getValue();
23733     },
23734
23735 //    /**
23736 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23737 //     * @method
23738 //     */
23739 //    markInvalid : Roo.emptyFn,
23740 //    /**
23741 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23742 //     * @method
23743 //     */
23744 //    clearInvalid : Roo.emptyFn,
23745
23746     setValue : function(v){
23747         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23748         this.editorcore.pushValue();
23749     },
23750
23751      
23752     // private
23753     deferFocus : function(){
23754         this.focus.defer(10, this);
23755     },
23756
23757     // doc'ed in Field
23758     focus : function(){
23759         this.editorcore.focus();
23760         
23761     },
23762       
23763
23764     // private
23765     onDestroy : function(){
23766         
23767         
23768         
23769         if(this.rendered){
23770             
23771             for (var i =0; i < this.toolbars.length;i++) {
23772                 // fixme - ask toolbars for heights?
23773                 this.toolbars[i].onDestroy();
23774             }
23775             
23776             this.wrap.dom.innerHTML = '';
23777             this.wrap.remove();
23778         }
23779     },
23780
23781     // private
23782     onFirstFocus : function(){
23783         //Roo.log("onFirstFocus");
23784         this.editorcore.onFirstFocus();
23785          for (var i =0; i < this.toolbars.length;i++) {
23786             this.toolbars[i].onFirstFocus();
23787         }
23788         
23789     },
23790     
23791     // private
23792     syncValue : function()
23793     {   
23794         this.editorcore.syncValue();
23795     },
23796     
23797     pushValue : function()
23798     {   
23799         this.editorcore.pushValue();
23800     }
23801      
23802     
23803     // hide stuff that is not compatible
23804     /**
23805      * @event blur
23806      * @hide
23807      */
23808     /**
23809      * @event change
23810      * @hide
23811      */
23812     /**
23813      * @event focus
23814      * @hide
23815      */
23816     /**
23817      * @event specialkey
23818      * @hide
23819      */
23820     /**
23821      * @cfg {String} fieldClass @hide
23822      */
23823     /**
23824      * @cfg {String} focusClass @hide
23825      */
23826     /**
23827      * @cfg {String} autoCreate @hide
23828      */
23829     /**
23830      * @cfg {String} inputType @hide
23831      */
23832     /**
23833      * @cfg {String} invalidClass @hide
23834      */
23835     /**
23836      * @cfg {String} invalidText @hide
23837      */
23838     /**
23839      * @cfg {String} msgFx @hide
23840      */
23841     /**
23842      * @cfg {String} validateOnBlur @hide
23843      */
23844 });
23845  
23846     
23847    
23848    
23849    
23850       
23851 Roo.namespace('Roo.bootstrap.htmleditor');
23852 /**
23853  * @class Roo.bootstrap.HtmlEditorToolbar1
23854  * Basic Toolbar
23855  * 
23856  * Usage:
23857  *
23858  new Roo.bootstrap.HtmlEditor({
23859     ....
23860     toolbars : [
23861         new Roo.bootstrap.HtmlEditorToolbar1({
23862             disable : { fonts: 1 , format: 1, ..., ... , ...],
23863             btns : [ .... ]
23864         })
23865     }
23866      
23867  * 
23868  * @cfg {Object} disable List of elements to disable..
23869  * @cfg {Array} btns List of additional buttons.
23870  * 
23871  * 
23872  * NEEDS Extra CSS? 
23873  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23874  */
23875  
23876 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23877 {
23878     
23879     Roo.apply(this, config);
23880     
23881     // default disabled, based on 'good practice'..
23882     this.disable = this.disable || {};
23883     Roo.applyIf(this.disable, {
23884         fontSize : true,
23885         colors : true,
23886         specialElements : true
23887     });
23888     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23889     
23890     this.editor = config.editor;
23891     this.editorcore = config.editor.editorcore;
23892     
23893     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23894     
23895     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23896     // dont call parent... till later.
23897 }
23898 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23899      
23900     bar : true,
23901     
23902     editor : false,
23903     editorcore : false,
23904     
23905     
23906     formats : [
23907         "p" ,  
23908         "h1","h2","h3","h4","h5","h6", 
23909         "pre", "code", 
23910         "abbr", "acronym", "address", "cite", "samp", "var",
23911         'div','span'
23912     ],
23913     
23914     onRender : function(ct, position)
23915     {
23916        // Roo.log("Call onRender: " + this.xtype);
23917         
23918        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23919        Roo.log(this.el);
23920        this.el.dom.style.marginBottom = '0';
23921        var _this = this;
23922        var editorcore = this.editorcore;
23923        var editor= this.editor;
23924        
23925        var children = [];
23926        var btn = function(id,cmd , toggle, handler, html){
23927        
23928             var  event = toggle ? 'toggle' : 'click';
23929        
23930             var a = {
23931                 size : 'sm',
23932                 xtype: 'Button',
23933                 xns: Roo.bootstrap,
23934                 glyphicon : id,
23935                 cmd : id || cmd,
23936                 enableToggle:toggle !== false,
23937                 html : html || '',
23938                 pressed : toggle ? false : null,
23939                 listeners : {}
23940             };
23941             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23942                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23943             };
23944             children.push(a);
23945             return a;
23946        }
23947        
23948     //    var cb_box = function...
23949         
23950         var style = {
23951                 xtype: 'Button',
23952                 size : 'sm',
23953                 xns: Roo.bootstrap,
23954                 glyphicon : 'font',
23955                 //html : 'submit'
23956                 menu : {
23957                     xtype: 'Menu',
23958                     xns: Roo.bootstrap,
23959                     items:  []
23960                 }
23961         };
23962         Roo.each(this.formats, function(f) {
23963             style.menu.items.push({
23964                 xtype :'MenuItem',
23965                 xns: Roo.bootstrap,
23966                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23967                 tagname : f,
23968                 listeners : {
23969                     click : function()
23970                     {
23971                         editorcore.insertTag(this.tagname);
23972                         editor.focus();
23973                     }
23974                 }
23975                 
23976             });
23977         });
23978         children.push(style);   
23979         
23980         btn('bold',false,true);
23981         btn('italic',false,true);
23982         btn('align-left', 'justifyleft',true);
23983         btn('align-center', 'justifycenter',true);
23984         btn('align-right' , 'justifyright',true);
23985         btn('link', false, false, function(btn) {
23986             //Roo.log("create link?");
23987             var url = prompt(this.createLinkText, this.defaultLinkValue);
23988             if(url && url != 'http:/'+'/'){
23989                 this.editorcore.relayCmd('createlink', url);
23990             }
23991         }),
23992         btn('list','insertunorderedlist',true);
23993         btn('pencil', false,true, function(btn){
23994                 Roo.log(this);
23995                 this.toggleSourceEdit(btn.pressed);
23996         });
23997         
23998         if (this.editor.btns.length > 0) {
23999             for (var i = 0; i<this.editor.btns.length; i++) {
24000                 children.push(this.editor.btns[i]);
24001             }
24002         }
24003         
24004         /*
24005         var cog = {
24006                 xtype: 'Button',
24007                 size : 'sm',
24008                 xns: Roo.bootstrap,
24009                 glyphicon : 'cog',
24010                 //html : 'submit'
24011                 menu : {
24012                     xtype: 'Menu',
24013                     xns: Roo.bootstrap,
24014                     items:  []
24015                 }
24016         };
24017         
24018         cog.menu.items.push({
24019             xtype :'MenuItem',
24020             xns: Roo.bootstrap,
24021             html : Clean styles,
24022             tagname : f,
24023             listeners : {
24024                 click : function()
24025                 {
24026                     editorcore.insertTag(this.tagname);
24027                     editor.focus();
24028                 }
24029             }
24030             
24031         });
24032        */
24033         
24034          
24035        this.xtype = 'NavSimplebar';
24036         
24037         for(var i=0;i< children.length;i++) {
24038             
24039             this.buttons.add(this.addxtypeChild(children[i]));
24040             
24041         }
24042         
24043         editor.on('editorevent', this.updateToolbar, this);
24044     },
24045     onBtnClick : function(id)
24046     {
24047        this.editorcore.relayCmd(id);
24048        this.editorcore.focus();
24049     },
24050     
24051     /**
24052      * Protected method that will not generally be called directly. It triggers
24053      * a toolbar update by reading the markup state of the current selection in the editor.
24054      */
24055     updateToolbar: function(){
24056
24057         if(!this.editorcore.activated){
24058             this.editor.onFirstFocus(); // is this neeed?
24059             return;
24060         }
24061
24062         var btns = this.buttons; 
24063         var doc = this.editorcore.doc;
24064         btns.get('bold').setActive(doc.queryCommandState('bold'));
24065         btns.get('italic').setActive(doc.queryCommandState('italic'));
24066         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24067         
24068         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24069         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24070         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24071         
24072         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24073         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24074          /*
24075         
24076         var ans = this.editorcore.getAllAncestors();
24077         if (this.formatCombo) {
24078             
24079             
24080             var store = this.formatCombo.store;
24081             this.formatCombo.setValue("");
24082             for (var i =0; i < ans.length;i++) {
24083                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24084                     // select it..
24085                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24086                     break;
24087                 }
24088             }
24089         }
24090         
24091         
24092         
24093         // hides menus... - so this cant be on a menu...
24094         Roo.bootstrap.MenuMgr.hideAll();
24095         */
24096         Roo.bootstrap.MenuMgr.hideAll();
24097         //this.editorsyncValue();
24098     },
24099     onFirstFocus: function() {
24100         this.buttons.each(function(item){
24101            item.enable();
24102         });
24103     },
24104     toggleSourceEdit : function(sourceEditMode){
24105         
24106           
24107         if(sourceEditMode){
24108             Roo.log("disabling buttons");
24109            this.buttons.each( function(item){
24110                 if(item.cmd != 'pencil'){
24111                     item.disable();
24112                 }
24113             });
24114           
24115         }else{
24116             Roo.log("enabling buttons");
24117             if(this.editorcore.initialized){
24118                 this.buttons.each( function(item){
24119                     item.enable();
24120                 });
24121             }
24122             
24123         }
24124         Roo.log("calling toggole on editor");
24125         // tell the editor that it's been pressed..
24126         this.editor.toggleSourceEdit(sourceEditMode);
24127        
24128     }
24129 });
24130
24131
24132
24133
24134
24135 /**
24136  * @class Roo.bootstrap.Table.AbstractSelectionModel
24137  * @extends Roo.util.Observable
24138  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24139  * implemented by descendant classes.  This class should not be directly instantiated.
24140  * @constructor
24141  */
24142 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24143     this.locked = false;
24144     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24145 };
24146
24147
24148 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24149     /** @ignore Called by the grid automatically. Do not call directly. */
24150     init : function(grid){
24151         this.grid = grid;
24152         this.initEvents();
24153     },
24154
24155     /**
24156      * Locks the selections.
24157      */
24158     lock : function(){
24159         this.locked = true;
24160     },
24161
24162     /**
24163      * Unlocks the selections.
24164      */
24165     unlock : function(){
24166         this.locked = false;
24167     },
24168
24169     /**
24170      * Returns true if the selections are locked.
24171      * @return {Boolean}
24172      */
24173     isLocked : function(){
24174         return this.locked;
24175     }
24176 });
24177 /**
24178  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24179  * @class Roo.bootstrap.Table.RowSelectionModel
24180  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24181  * It supports multiple selections and keyboard selection/navigation. 
24182  * @constructor
24183  * @param {Object} config
24184  */
24185
24186 Roo.bootstrap.Table.RowSelectionModel = function(config){
24187     Roo.apply(this, config);
24188     this.selections = new Roo.util.MixedCollection(false, function(o){
24189         return o.id;
24190     });
24191
24192     this.last = false;
24193     this.lastActive = false;
24194
24195     this.addEvents({
24196         /**
24197              * @event selectionchange
24198              * Fires when the selection changes
24199              * @param {SelectionModel} this
24200              */
24201             "selectionchange" : true,
24202         /**
24203              * @event afterselectionchange
24204              * Fires after the selection changes (eg. by key press or clicking)
24205              * @param {SelectionModel} this
24206              */
24207             "afterselectionchange" : true,
24208         /**
24209              * @event beforerowselect
24210              * Fires when a row is selected being selected, return false to cancel.
24211              * @param {SelectionModel} this
24212              * @param {Number} rowIndex The selected index
24213              * @param {Boolean} keepExisting False if other selections will be cleared
24214              */
24215             "beforerowselect" : true,
24216         /**
24217              * @event rowselect
24218              * Fires when a row is selected.
24219              * @param {SelectionModel} this
24220              * @param {Number} rowIndex The selected index
24221              * @param {Roo.data.Record} r The record
24222              */
24223             "rowselect" : true,
24224         /**
24225              * @event rowdeselect
24226              * Fires when a row is deselected.
24227              * @param {SelectionModel} this
24228              * @param {Number} rowIndex The selected index
24229              */
24230         "rowdeselect" : true
24231     });
24232     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24233     this.locked = false;
24234  };
24235
24236 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24237     /**
24238      * @cfg {Boolean} singleSelect
24239      * True to allow selection of only one row at a time (defaults to false)
24240      */
24241     singleSelect : false,
24242
24243     // private
24244     initEvents : function()
24245     {
24246
24247         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24248         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24249         //}else{ // allow click to work like normal
24250          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24251         //}
24252         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24253         this.grid.on("rowclick", this.handleMouseDown, this);
24254         
24255         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24256             "up" : function(e){
24257                 if(!e.shiftKey){
24258                     this.selectPrevious(e.shiftKey);
24259                 }else if(this.last !== false && this.lastActive !== false){
24260                     var last = this.last;
24261                     this.selectRange(this.last,  this.lastActive-1);
24262                     this.grid.getView().focusRow(this.lastActive);
24263                     if(last !== false){
24264                         this.last = last;
24265                     }
24266                 }else{
24267                     this.selectFirstRow();
24268                 }
24269                 this.fireEvent("afterselectionchange", this);
24270             },
24271             "down" : function(e){
24272                 if(!e.shiftKey){
24273                     this.selectNext(e.shiftKey);
24274                 }else if(this.last !== false && this.lastActive !== false){
24275                     var last = this.last;
24276                     this.selectRange(this.last,  this.lastActive+1);
24277                     this.grid.getView().focusRow(this.lastActive);
24278                     if(last !== false){
24279                         this.last = last;
24280                     }
24281                 }else{
24282                     this.selectFirstRow();
24283                 }
24284                 this.fireEvent("afterselectionchange", this);
24285             },
24286             scope: this
24287         });
24288         this.grid.store.on('load', function(){
24289             this.selections.clear();
24290         },this);
24291         /*
24292         var view = this.grid.view;
24293         view.on("refresh", this.onRefresh, this);
24294         view.on("rowupdated", this.onRowUpdated, this);
24295         view.on("rowremoved", this.onRemove, this);
24296         */
24297     },
24298
24299     // private
24300     onRefresh : function()
24301     {
24302         var ds = this.grid.store, i, v = this.grid.view;
24303         var s = this.selections;
24304         s.each(function(r){
24305             if((i = ds.indexOfId(r.id)) != -1){
24306                 v.onRowSelect(i);
24307             }else{
24308                 s.remove(r);
24309             }
24310         });
24311     },
24312
24313     // private
24314     onRemove : function(v, index, r){
24315         this.selections.remove(r);
24316     },
24317
24318     // private
24319     onRowUpdated : function(v, index, r){
24320         if(this.isSelected(r)){
24321             v.onRowSelect(index);
24322         }
24323     },
24324
24325     /**
24326      * Select records.
24327      * @param {Array} records The records to select
24328      * @param {Boolean} keepExisting (optional) True to keep existing selections
24329      */
24330     selectRecords : function(records, keepExisting)
24331     {
24332         if(!keepExisting){
24333             this.clearSelections();
24334         }
24335             var ds = this.grid.store;
24336         for(var i = 0, len = records.length; i < len; i++){
24337             this.selectRow(ds.indexOf(records[i]), true);
24338         }
24339     },
24340
24341     /**
24342      * Gets the number of selected rows.
24343      * @return {Number}
24344      */
24345     getCount : function(){
24346         return this.selections.length;
24347     },
24348
24349     /**
24350      * Selects the first row in the grid.
24351      */
24352     selectFirstRow : function(){
24353         this.selectRow(0);
24354     },
24355
24356     /**
24357      * Select the last row.
24358      * @param {Boolean} keepExisting (optional) True to keep existing selections
24359      */
24360     selectLastRow : function(keepExisting){
24361         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24362         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24363     },
24364
24365     /**
24366      * Selects the row immediately following the last selected row.
24367      * @param {Boolean} keepExisting (optional) True to keep existing selections
24368      */
24369     selectNext : function(keepExisting)
24370     {
24371             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24372             this.selectRow(this.last+1, keepExisting);
24373             this.grid.getView().focusRow(this.last);
24374         }
24375     },
24376
24377     /**
24378      * Selects the row that precedes the last selected row.
24379      * @param {Boolean} keepExisting (optional) True to keep existing selections
24380      */
24381     selectPrevious : function(keepExisting){
24382         if(this.last){
24383             this.selectRow(this.last-1, keepExisting);
24384             this.grid.getView().focusRow(this.last);
24385         }
24386     },
24387
24388     /**
24389      * Returns the selected records
24390      * @return {Array} Array of selected records
24391      */
24392     getSelections : function(){
24393         return [].concat(this.selections.items);
24394     },
24395
24396     /**
24397      * Returns the first selected record.
24398      * @return {Record}
24399      */
24400     getSelected : function(){
24401         return this.selections.itemAt(0);
24402     },
24403
24404
24405     /**
24406      * Clears all selections.
24407      */
24408     clearSelections : function(fast)
24409     {
24410         if(this.locked) {
24411             return;
24412         }
24413         if(fast !== true){
24414                 var ds = this.grid.store;
24415             var s = this.selections;
24416             s.each(function(r){
24417                 this.deselectRow(ds.indexOfId(r.id));
24418             }, this);
24419             s.clear();
24420         }else{
24421             this.selections.clear();
24422         }
24423         this.last = false;
24424     },
24425
24426
24427     /**
24428      * Selects all rows.
24429      */
24430     selectAll : function(){
24431         if(this.locked) {
24432             return;
24433         }
24434         this.selections.clear();
24435         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24436             this.selectRow(i, true);
24437         }
24438     },
24439
24440     /**
24441      * Returns True if there is a selection.
24442      * @return {Boolean}
24443      */
24444     hasSelection : function(){
24445         return this.selections.length > 0;
24446     },
24447
24448     /**
24449      * Returns True if the specified row is selected.
24450      * @param {Number/Record} record The record or index of the record to check
24451      * @return {Boolean}
24452      */
24453     isSelected : function(index){
24454             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24455         return (r && this.selections.key(r.id) ? true : false);
24456     },
24457
24458     /**
24459      * Returns True if the specified record id is selected.
24460      * @param {String} id The id of record to check
24461      * @return {Boolean}
24462      */
24463     isIdSelected : function(id){
24464         return (this.selections.key(id) ? true : false);
24465     },
24466
24467
24468     // private
24469     handleMouseDBClick : function(e, t){
24470         
24471     },
24472     // private
24473     handleMouseDown : function(e, t)
24474     {
24475             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24476         if(this.isLocked() || rowIndex < 0 ){
24477             return;
24478         };
24479         if(e.shiftKey && this.last !== false){
24480             var last = this.last;
24481             this.selectRange(last, rowIndex, e.ctrlKey);
24482             this.last = last; // reset the last
24483             t.focus();
24484     
24485         }else{
24486             var isSelected = this.isSelected(rowIndex);
24487             //Roo.log("select row:" + rowIndex);
24488             if(isSelected){
24489                 this.deselectRow(rowIndex);
24490             } else {
24491                         this.selectRow(rowIndex, true);
24492             }
24493     
24494             /*
24495                 if(e.button !== 0 && isSelected){
24496                 alert('rowIndex 2: ' + rowIndex);
24497                     view.focusRow(rowIndex);
24498                 }else if(e.ctrlKey && isSelected){
24499                     this.deselectRow(rowIndex);
24500                 }else if(!isSelected){
24501                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24502                     view.focusRow(rowIndex);
24503                 }
24504             */
24505         }
24506         this.fireEvent("afterselectionchange", this);
24507     },
24508     // private
24509     handleDragableRowClick :  function(grid, rowIndex, e) 
24510     {
24511         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24512             this.selectRow(rowIndex, false);
24513             grid.view.focusRow(rowIndex);
24514              this.fireEvent("afterselectionchange", this);
24515         }
24516     },
24517     
24518     /**
24519      * Selects multiple rows.
24520      * @param {Array} rows Array of the indexes of the row to select
24521      * @param {Boolean} keepExisting (optional) True to keep existing selections
24522      */
24523     selectRows : function(rows, keepExisting){
24524         if(!keepExisting){
24525             this.clearSelections();
24526         }
24527         for(var i = 0, len = rows.length; i < len; i++){
24528             this.selectRow(rows[i], true);
24529         }
24530     },
24531
24532     /**
24533      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24534      * @param {Number} startRow The index of the first row in the range
24535      * @param {Number} endRow The index of the last row in the range
24536      * @param {Boolean} keepExisting (optional) True to retain existing selections
24537      */
24538     selectRange : function(startRow, endRow, keepExisting){
24539         if(this.locked) {
24540             return;
24541         }
24542         if(!keepExisting){
24543             this.clearSelections();
24544         }
24545         if(startRow <= endRow){
24546             for(var i = startRow; i <= endRow; i++){
24547                 this.selectRow(i, true);
24548             }
24549         }else{
24550             for(var i = startRow; i >= endRow; i--){
24551                 this.selectRow(i, true);
24552             }
24553         }
24554     },
24555
24556     /**
24557      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24558      * @param {Number} startRow The index of the first row in the range
24559      * @param {Number} endRow The index of the last row in the range
24560      */
24561     deselectRange : function(startRow, endRow, preventViewNotify){
24562         if(this.locked) {
24563             return;
24564         }
24565         for(var i = startRow; i <= endRow; i++){
24566             this.deselectRow(i, preventViewNotify);
24567         }
24568     },
24569
24570     /**
24571      * Selects a row.
24572      * @param {Number} row The index of the row to select
24573      * @param {Boolean} keepExisting (optional) True to keep existing selections
24574      */
24575     selectRow : function(index, keepExisting, preventViewNotify)
24576     {
24577             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24578             return;
24579         }
24580         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24581             if(!keepExisting || this.singleSelect){
24582                 this.clearSelections();
24583             }
24584             
24585             var r = this.grid.store.getAt(index);
24586             //console.log('selectRow - record id :' + r.id);
24587             
24588             this.selections.add(r);
24589             this.last = this.lastActive = index;
24590             if(!preventViewNotify){
24591                 var proxy = new Roo.Element(
24592                                 this.grid.getRowDom(index)
24593                 );
24594                 proxy.addClass('bg-info info');
24595             }
24596             this.fireEvent("rowselect", this, index, r);
24597             this.fireEvent("selectionchange", this);
24598         }
24599     },
24600
24601     /**
24602      * Deselects a row.
24603      * @param {Number} row The index of the row to deselect
24604      */
24605     deselectRow : function(index, preventViewNotify)
24606     {
24607         if(this.locked) {
24608             return;
24609         }
24610         if(this.last == index){
24611             this.last = false;
24612         }
24613         if(this.lastActive == index){
24614             this.lastActive = false;
24615         }
24616         
24617         var r = this.grid.store.getAt(index);
24618         if (!r) {
24619             return;
24620         }
24621         
24622         this.selections.remove(r);
24623         //.console.log('deselectRow - record id :' + r.id);
24624         if(!preventViewNotify){
24625         
24626             var proxy = new Roo.Element(
24627                 this.grid.getRowDom(index)
24628             );
24629             proxy.removeClass('bg-info info');
24630         }
24631         this.fireEvent("rowdeselect", this, index);
24632         this.fireEvent("selectionchange", this);
24633     },
24634
24635     // private
24636     restoreLast : function(){
24637         if(this._last){
24638             this.last = this._last;
24639         }
24640     },
24641
24642     // private
24643     acceptsNav : function(row, col, cm){
24644         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24645     },
24646
24647     // private
24648     onEditorKey : function(field, e){
24649         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24650         if(k == e.TAB){
24651             e.stopEvent();
24652             ed.completeEdit();
24653             if(e.shiftKey){
24654                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24655             }else{
24656                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24657             }
24658         }else if(k == e.ENTER && !e.ctrlKey){
24659             e.stopEvent();
24660             ed.completeEdit();
24661             if(e.shiftKey){
24662                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24663             }else{
24664                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24665             }
24666         }else if(k == e.ESC){
24667             ed.cancelEdit();
24668         }
24669         if(newCell){
24670             g.startEditing(newCell[0], newCell[1]);
24671         }
24672     }
24673 });
24674 /*
24675  * Based on:
24676  * Ext JS Library 1.1.1
24677  * Copyright(c) 2006-2007, Ext JS, LLC.
24678  *
24679  * Originally Released Under LGPL - original licence link has changed is not relivant.
24680  *
24681  * Fork - LGPL
24682  * <script type="text/javascript">
24683  */
24684  
24685 /**
24686  * @class Roo.bootstrap.PagingToolbar
24687  * @extends Roo.bootstrap.NavSimplebar
24688  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24689  * @constructor
24690  * Create a new PagingToolbar
24691  * @param {Object} config The config object
24692  * @param {Roo.data.Store} store
24693  */
24694 Roo.bootstrap.PagingToolbar = function(config)
24695 {
24696     // old args format still supported... - xtype is prefered..
24697         // created from xtype...
24698     
24699     this.ds = config.dataSource;
24700     
24701     if (config.store && !this.ds) {
24702         this.store= Roo.factory(config.store, Roo.data);
24703         this.ds = this.store;
24704         this.ds.xmodule = this.xmodule || false;
24705     }
24706     
24707     this.toolbarItems = [];
24708     if (config.items) {
24709         this.toolbarItems = config.items;
24710     }
24711     
24712     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24713     
24714     this.cursor = 0;
24715     
24716     if (this.ds) { 
24717         this.bind(this.ds);
24718     }
24719     
24720     if (Roo.bootstrap.version == 4) {
24721         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24722     } else {
24723         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24724     }
24725     
24726 };
24727
24728 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24729     /**
24730      * @cfg {Roo.data.Store} dataSource
24731      * The underlying data store providing the paged data
24732      */
24733     /**
24734      * @cfg {String/HTMLElement/Element} container
24735      * container The id or element that will contain the toolbar
24736      */
24737     /**
24738      * @cfg {Boolean} displayInfo
24739      * True to display the displayMsg (defaults to false)
24740      */
24741     /**
24742      * @cfg {Number} pageSize
24743      * The number of records to display per page (defaults to 20)
24744      */
24745     pageSize: 20,
24746     /**
24747      * @cfg {String} displayMsg
24748      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24749      */
24750     displayMsg : 'Displaying {0} - {1} of {2}',
24751     /**
24752      * @cfg {String} emptyMsg
24753      * The message to display when no records are found (defaults to "No data to display")
24754      */
24755     emptyMsg : 'No data to display',
24756     /**
24757      * Customizable piece of the default paging text (defaults to "Page")
24758      * @type String
24759      */
24760     beforePageText : "Page",
24761     /**
24762      * Customizable piece of the default paging text (defaults to "of %0")
24763      * @type String
24764      */
24765     afterPageText : "of {0}",
24766     /**
24767      * Customizable piece of the default paging text (defaults to "First Page")
24768      * @type String
24769      */
24770     firstText : "First Page",
24771     /**
24772      * Customizable piece of the default paging text (defaults to "Previous Page")
24773      * @type String
24774      */
24775     prevText : "Previous Page",
24776     /**
24777      * Customizable piece of the default paging text (defaults to "Next Page")
24778      * @type String
24779      */
24780     nextText : "Next Page",
24781     /**
24782      * Customizable piece of the default paging text (defaults to "Last Page")
24783      * @type String
24784      */
24785     lastText : "Last Page",
24786     /**
24787      * Customizable piece of the default paging text (defaults to "Refresh")
24788      * @type String
24789      */
24790     refreshText : "Refresh",
24791
24792     buttons : false,
24793     // private
24794     onRender : function(ct, position) 
24795     {
24796         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24797         this.navgroup.parentId = this.id;
24798         this.navgroup.onRender(this.el, null);
24799         // add the buttons to the navgroup
24800         
24801         if(this.displayInfo){
24802             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24803             this.displayEl = this.el.select('.x-paging-info', true).first();
24804 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24805 //            this.displayEl = navel.el.select('span',true).first();
24806         }
24807         
24808         var _this = this;
24809         
24810         if(this.buttons){
24811             Roo.each(_this.buttons, function(e){ // this might need to use render????
24812                Roo.factory(e).render(_this.el);
24813             });
24814         }
24815             
24816         Roo.each(_this.toolbarItems, function(e) {
24817             _this.navgroup.addItem(e);
24818         });
24819         
24820         
24821         this.first = this.navgroup.addItem({
24822             tooltip: this.firstText,
24823             cls: "prev btn-outline-secondary",
24824             html : ' <i class="fa fa-step-backward"></i>',
24825             disabled: true,
24826             preventDefault: true,
24827             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24828         });
24829         
24830         this.prev =  this.navgroup.addItem({
24831             tooltip: this.prevText,
24832             cls: "prev btn-outline-secondary",
24833             html : ' <i class="fa fa-backward"></i>',
24834             disabled: true,
24835             preventDefault: true,
24836             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24837         });
24838     //this.addSeparator();
24839         
24840         
24841         var field = this.navgroup.addItem( {
24842             tagtype : 'span',
24843             cls : 'x-paging-position  btn-outline-secondary',
24844              disabled: true,
24845             html : this.beforePageText  +
24846                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24847                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24848          } ); //?? escaped?
24849         
24850         this.field = field.el.select('input', true).first();
24851         this.field.on("keydown", this.onPagingKeydown, this);
24852         this.field.on("focus", function(){this.dom.select();});
24853     
24854     
24855         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24856         //this.field.setHeight(18);
24857         //this.addSeparator();
24858         this.next = this.navgroup.addItem({
24859             tooltip: this.nextText,
24860             cls: "next btn-outline-secondary",
24861             html : ' <i class="fa fa-forward"></i>',
24862             disabled: true,
24863             preventDefault: true,
24864             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24865         });
24866         this.last = this.navgroup.addItem({
24867             tooltip: this.lastText,
24868             html : ' <i class="fa fa-step-forward"></i>',
24869             cls: "next btn-outline-secondary",
24870             disabled: true,
24871             preventDefault: true,
24872             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24873         });
24874     //this.addSeparator();
24875         this.loading = this.navgroup.addItem({
24876             tooltip: this.refreshText,
24877             cls: "btn-outline-secondary",
24878             html : ' <i class="fa fa-refresh"></i>',
24879             preventDefault: true,
24880             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24881         });
24882         
24883     },
24884
24885     // private
24886     updateInfo : function(){
24887         if(this.displayEl){
24888             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24889             var msg = count == 0 ?
24890                 this.emptyMsg :
24891                 String.format(
24892                     this.displayMsg,
24893                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24894                 );
24895             this.displayEl.update(msg);
24896         }
24897     },
24898
24899     // private
24900     onLoad : function(ds, r, o)
24901     {
24902         this.cursor = o.params.start ? o.params.start : 0;
24903         
24904         var d = this.getPageData(),
24905             ap = d.activePage,
24906             ps = d.pages;
24907         
24908         
24909         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24910         this.field.dom.value = ap;
24911         this.first.setDisabled(ap == 1);
24912         this.prev.setDisabled(ap == 1);
24913         this.next.setDisabled(ap == ps);
24914         this.last.setDisabled(ap == ps);
24915         this.loading.enable();
24916         this.updateInfo();
24917     },
24918
24919     // private
24920     getPageData : function(){
24921         var total = this.ds.getTotalCount();
24922         return {
24923             total : total,
24924             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24925             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24926         };
24927     },
24928
24929     // private
24930     onLoadError : function(){
24931         this.loading.enable();
24932     },
24933
24934     // private
24935     onPagingKeydown : function(e){
24936         var k = e.getKey();
24937         var d = this.getPageData();
24938         if(k == e.RETURN){
24939             var v = this.field.dom.value, pageNum;
24940             if(!v || isNaN(pageNum = parseInt(v, 10))){
24941                 this.field.dom.value = d.activePage;
24942                 return;
24943             }
24944             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24945             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24946             e.stopEvent();
24947         }
24948         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))
24949         {
24950           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24951           this.field.dom.value = pageNum;
24952           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24953           e.stopEvent();
24954         }
24955         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24956         {
24957           var v = this.field.dom.value, pageNum; 
24958           var increment = (e.shiftKey) ? 10 : 1;
24959           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24960                 increment *= -1;
24961           }
24962           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24963             this.field.dom.value = d.activePage;
24964             return;
24965           }
24966           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24967           {
24968             this.field.dom.value = parseInt(v, 10) + increment;
24969             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24970             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24971           }
24972           e.stopEvent();
24973         }
24974     },
24975
24976     // private
24977     beforeLoad : function(){
24978         if(this.loading){
24979             this.loading.disable();
24980         }
24981     },
24982
24983     // private
24984     onClick : function(which){
24985         
24986         var ds = this.ds;
24987         if (!ds) {
24988             return;
24989         }
24990         
24991         switch(which){
24992             case "first":
24993                 ds.load({params:{start: 0, limit: this.pageSize}});
24994             break;
24995             case "prev":
24996                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24997             break;
24998             case "next":
24999                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25000             break;
25001             case "last":
25002                 var total = ds.getTotalCount();
25003                 var extra = total % this.pageSize;
25004                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25005                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25006             break;
25007             case "refresh":
25008                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25009             break;
25010         }
25011     },
25012
25013     /**
25014      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25015      * @param {Roo.data.Store} store The data store to unbind
25016      */
25017     unbind : function(ds){
25018         ds.un("beforeload", this.beforeLoad, this);
25019         ds.un("load", this.onLoad, this);
25020         ds.un("loadexception", this.onLoadError, this);
25021         ds.un("remove", this.updateInfo, this);
25022         ds.un("add", this.updateInfo, this);
25023         this.ds = undefined;
25024     },
25025
25026     /**
25027      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25028      * @param {Roo.data.Store} store The data store to bind
25029      */
25030     bind : function(ds){
25031         ds.on("beforeload", this.beforeLoad, this);
25032         ds.on("load", this.onLoad, this);
25033         ds.on("loadexception", this.onLoadError, this);
25034         ds.on("remove", this.updateInfo, this);
25035         ds.on("add", this.updateInfo, this);
25036         this.ds = ds;
25037     }
25038 });/*
25039  * - LGPL
25040  *
25041  * element
25042  * 
25043  */
25044
25045 /**
25046  * @class Roo.bootstrap.MessageBar
25047  * @extends Roo.bootstrap.Component
25048  * Bootstrap MessageBar class
25049  * @cfg {String} html contents of the MessageBar
25050  * @cfg {String} weight (info | success | warning | danger) default info
25051  * @cfg {String} beforeClass insert the bar before the given class
25052  * @cfg {Boolean} closable (true | false) default false
25053  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25054  * 
25055  * @constructor
25056  * Create a new Element
25057  * @param {Object} config The config object
25058  */
25059
25060 Roo.bootstrap.MessageBar = function(config){
25061     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25062 };
25063
25064 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25065     
25066     html: '',
25067     weight: 'info',
25068     closable: false,
25069     fixed: false,
25070     beforeClass: 'bootstrap-sticky-wrap',
25071     
25072     getAutoCreate : function(){
25073         
25074         var cfg = {
25075             tag: 'div',
25076             cls: 'alert alert-dismissable alert-' + this.weight,
25077             cn: [
25078                 {
25079                     tag: 'span',
25080                     cls: 'message',
25081                     html: this.html || ''
25082                 }
25083             ]
25084         };
25085         
25086         if(this.fixed){
25087             cfg.cls += ' alert-messages-fixed';
25088         }
25089         
25090         if(this.closable){
25091             cfg.cn.push({
25092                 tag: 'button',
25093                 cls: 'close',
25094                 html: 'x'
25095             });
25096         }
25097         
25098         return cfg;
25099     },
25100     
25101     onRender : function(ct, position)
25102     {
25103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25104         
25105         if(!this.el){
25106             var cfg = Roo.apply({},  this.getAutoCreate());
25107             cfg.id = Roo.id();
25108             
25109             if (this.cls) {
25110                 cfg.cls += ' ' + this.cls;
25111             }
25112             if (this.style) {
25113                 cfg.style = this.style;
25114             }
25115             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25116             
25117             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25118         }
25119         
25120         this.el.select('>button.close').on('click', this.hide, this);
25121         
25122     },
25123     
25124     show : function()
25125     {
25126         if (!this.rendered) {
25127             this.render();
25128         }
25129         
25130         this.el.show();
25131         
25132         this.fireEvent('show', this);
25133         
25134     },
25135     
25136     hide : function()
25137     {
25138         if (!this.rendered) {
25139             this.render();
25140         }
25141         
25142         this.el.hide();
25143         
25144         this.fireEvent('hide', this);
25145     },
25146     
25147     update : function()
25148     {
25149 //        var e = this.el.dom.firstChild;
25150 //        
25151 //        if(this.closable){
25152 //            e = e.nextSibling;
25153 //        }
25154 //        
25155 //        e.data = this.html || '';
25156
25157         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25158     }
25159    
25160 });
25161
25162  
25163
25164      /*
25165  * - LGPL
25166  *
25167  * Graph
25168  * 
25169  */
25170
25171
25172 /**
25173  * @class Roo.bootstrap.Graph
25174  * @extends Roo.bootstrap.Component
25175  * Bootstrap Graph class
25176 > Prameters
25177  -sm {number} sm 4
25178  -md {number} md 5
25179  @cfg {String} graphtype  bar | vbar | pie
25180  @cfg {number} g_x coodinator | centre x (pie)
25181  @cfg {number} g_y coodinator | centre y (pie)
25182  @cfg {number} g_r radius (pie)
25183  @cfg {number} g_height height of the chart (respected by all elements in the set)
25184  @cfg {number} g_width width of the chart (respected by all elements in the set)
25185  @cfg {Object} title The title of the chart
25186     
25187  -{Array}  values
25188  -opts (object) options for the chart 
25189      o {
25190      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25191      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25192      o vgutter (number)
25193      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.
25194      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25195      o to
25196      o stretch (boolean)
25197      o }
25198  -opts (object) options for the pie
25199      o{
25200      o cut
25201      o startAngle (number)
25202      o endAngle (number)
25203      } 
25204  *
25205  * @constructor
25206  * Create a new Input
25207  * @param {Object} config The config object
25208  */
25209
25210 Roo.bootstrap.Graph = function(config){
25211     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25212     
25213     this.addEvents({
25214         // img events
25215         /**
25216          * @event click
25217          * The img click event for the img.
25218          * @param {Roo.EventObject} e
25219          */
25220         "click" : true
25221     });
25222 };
25223
25224 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25225     
25226     sm: 4,
25227     md: 5,
25228     graphtype: 'bar',
25229     g_height: 250,
25230     g_width: 400,
25231     g_x: 50,
25232     g_y: 50,
25233     g_r: 30,
25234     opts:{
25235         //g_colors: this.colors,
25236         g_type: 'soft',
25237         g_gutter: '20%'
25238
25239     },
25240     title : false,
25241
25242     getAutoCreate : function(){
25243         
25244         var cfg = {
25245             tag: 'div',
25246             html : null
25247         };
25248         
25249         
25250         return  cfg;
25251     },
25252
25253     onRender : function(ct,position){
25254         
25255         
25256         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25257         
25258         if (typeof(Raphael) == 'undefined') {
25259             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25260             return;
25261         }
25262         
25263         this.raphael = Raphael(this.el.dom);
25264         
25265                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25266                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25267                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25268                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25269                 /*
25270                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25271                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25272                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25273                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25274                 
25275                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25276                 r.barchart(330, 10, 300, 220, data1);
25277                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25278                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25279                 */
25280                 
25281                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25282                 // r.barchart(30, 30, 560, 250,  xdata, {
25283                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25284                 //     axis : "0 0 1 1",
25285                 //     axisxlabels :  xdata
25286                 //     //yvalues : cols,
25287                    
25288                 // });
25289 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25290 //        
25291 //        this.load(null,xdata,{
25292 //                axis : "0 0 1 1",
25293 //                axisxlabels :  xdata
25294 //                });
25295
25296     },
25297
25298     load : function(graphtype,xdata,opts)
25299     {
25300         this.raphael.clear();
25301         if(!graphtype) {
25302             graphtype = this.graphtype;
25303         }
25304         if(!opts){
25305             opts = this.opts;
25306         }
25307         var r = this.raphael,
25308             fin = function () {
25309                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25310             },
25311             fout = function () {
25312                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25313             },
25314             pfin = function() {
25315                 this.sector.stop();
25316                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25317
25318                 if (this.label) {
25319                     this.label[0].stop();
25320                     this.label[0].attr({ r: 7.5 });
25321                     this.label[1].attr({ "font-weight": 800 });
25322                 }
25323             },
25324             pfout = function() {
25325                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25326
25327                 if (this.label) {
25328                     this.label[0].animate({ r: 5 }, 500, "bounce");
25329                     this.label[1].attr({ "font-weight": 400 });
25330                 }
25331             };
25332
25333         switch(graphtype){
25334             case 'bar':
25335                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25336                 break;
25337             case 'hbar':
25338                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25339                 break;
25340             case 'pie':
25341 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25342 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25343 //            
25344                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25345                 
25346                 break;
25347
25348         }
25349         
25350         if(this.title){
25351             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25352         }
25353         
25354     },
25355     
25356     setTitle: function(o)
25357     {
25358         this.title = o;
25359     },
25360     
25361     initEvents: function() {
25362         
25363         if(!this.href){
25364             this.el.on('click', this.onClick, this);
25365         }
25366     },
25367     
25368     onClick : function(e)
25369     {
25370         Roo.log('img onclick');
25371         this.fireEvent('click', this, e);
25372     }
25373    
25374 });
25375
25376  
25377 /*
25378  * - LGPL
25379  *
25380  * numberBox
25381  * 
25382  */
25383 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25384
25385 /**
25386  * @class Roo.bootstrap.dash.NumberBox
25387  * @extends Roo.bootstrap.Component
25388  * Bootstrap NumberBox class
25389  * @cfg {String} headline Box headline
25390  * @cfg {String} content Box content
25391  * @cfg {String} icon Box icon
25392  * @cfg {String} footer Footer text
25393  * @cfg {String} fhref Footer href
25394  * 
25395  * @constructor
25396  * Create a new NumberBox
25397  * @param {Object} config The config object
25398  */
25399
25400
25401 Roo.bootstrap.dash.NumberBox = function(config){
25402     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25403     
25404 };
25405
25406 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25407     
25408     headline : '',
25409     content : '',
25410     icon : '',
25411     footer : '',
25412     fhref : '',
25413     ficon : '',
25414     
25415     getAutoCreate : function(){
25416         
25417         var cfg = {
25418             tag : 'div',
25419             cls : 'small-box ',
25420             cn : [
25421                 {
25422                     tag : 'div',
25423                     cls : 'inner',
25424                     cn :[
25425                         {
25426                             tag : 'h3',
25427                             cls : 'roo-headline',
25428                             html : this.headline
25429                         },
25430                         {
25431                             tag : 'p',
25432                             cls : 'roo-content',
25433                             html : this.content
25434                         }
25435                     ]
25436                 }
25437             ]
25438         };
25439         
25440         if(this.icon){
25441             cfg.cn.push({
25442                 tag : 'div',
25443                 cls : 'icon',
25444                 cn :[
25445                     {
25446                         tag : 'i',
25447                         cls : 'ion ' + this.icon
25448                     }
25449                 ]
25450             });
25451         }
25452         
25453         if(this.footer){
25454             var footer = {
25455                 tag : 'a',
25456                 cls : 'small-box-footer',
25457                 href : this.fhref || '#',
25458                 html : this.footer
25459             };
25460             
25461             cfg.cn.push(footer);
25462             
25463         }
25464         
25465         return  cfg;
25466     },
25467
25468     onRender : function(ct,position){
25469         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25470
25471
25472        
25473                 
25474     },
25475
25476     setHeadline: function (value)
25477     {
25478         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25479     },
25480     
25481     setFooter: function (value, href)
25482     {
25483         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25484         
25485         if(href){
25486             this.el.select('a.small-box-footer',true).first().attr('href', href);
25487         }
25488         
25489     },
25490
25491     setContent: function (value)
25492     {
25493         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25494     },
25495
25496     initEvents: function() 
25497     {   
25498         
25499     }
25500     
25501 });
25502
25503  
25504 /*
25505  * - LGPL
25506  *
25507  * TabBox
25508  * 
25509  */
25510 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25511
25512 /**
25513  * @class Roo.bootstrap.dash.TabBox
25514  * @extends Roo.bootstrap.Component
25515  * Bootstrap TabBox class
25516  * @cfg {String} title Title of the TabBox
25517  * @cfg {String} icon Icon of the TabBox
25518  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25519  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25520  * 
25521  * @constructor
25522  * Create a new TabBox
25523  * @param {Object} config The config object
25524  */
25525
25526
25527 Roo.bootstrap.dash.TabBox = function(config){
25528     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25529     this.addEvents({
25530         // raw events
25531         /**
25532          * @event addpane
25533          * When a pane is added
25534          * @param {Roo.bootstrap.dash.TabPane} pane
25535          */
25536         "addpane" : true,
25537         /**
25538          * @event activatepane
25539          * When a pane is activated
25540          * @param {Roo.bootstrap.dash.TabPane} pane
25541          */
25542         "activatepane" : true
25543         
25544          
25545     });
25546     
25547     this.panes = [];
25548 };
25549
25550 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25551
25552     title : '',
25553     icon : false,
25554     showtabs : true,
25555     tabScrollable : false,
25556     
25557     getChildContainer : function()
25558     {
25559         return this.el.select('.tab-content', true).first();
25560     },
25561     
25562     getAutoCreate : function(){
25563         
25564         var header = {
25565             tag: 'li',
25566             cls: 'pull-left header',
25567             html: this.title,
25568             cn : []
25569         };
25570         
25571         if(this.icon){
25572             header.cn.push({
25573                 tag: 'i',
25574                 cls: 'fa ' + this.icon
25575             });
25576         }
25577         
25578         var h = {
25579             tag: 'ul',
25580             cls: 'nav nav-tabs pull-right',
25581             cn: [
25582                 header
25583             ]
25584         };
25585         
25586         if(this.tabScrollable){
25587             h = {
25588                 tag: 'div',
25589                 cls: 'tab-header',
25590                 cn: [
25591                     {
25592                         tag: 'ul',
25593                         cls: 'nav nav-tabs pull-right',
25594                         cn: [
25595                             header
25596                         ]
25597                     }
25598                 ]
25599             };
25600         }
25601         
25602         var cfg = {
25603             tag: 'div',
25604             cls: 'nav-tabs-custom',
25605             cn: [
25606                 h,
25607                 {
25608                     tag: 'div',
25609                     cls: 'tab-content no-padding',
25610                     cn: []
25611                 }
25612             ]
25613         };
25614
25615         return  cfg;
25616     },
25617     initEvents : function()
25618     {
25619         //Roo.log('add add pane handler');
25620         this.on('addpane', this.onAddPane, this);
25621     },
25622      /**
25623      * Updates the box title
25624      * @param {String} html to set the title to.
25625      */
25626     setTitle : function(value)
25627     {
25628         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25629     },
25630     onAddPane : function(pane)
25631     {
25632         this.panes.push(pane);
25633         //Roo.log('addpane');
25634         //Roo.log(pane);
25635         // tabs are rendere left to right..
25636         if(!this.showtabs){
25637             return;
25638         }
25639         
25640         var ctr = this.el.select('.nav-tabs', true).first();
25641          
25642          
25643         var existing = ctr.select('.nav-tab',true);
25644         var qty = existing.getCount();;
25645         
25646         
25647         var tab = ctr.createChild({
25648             tag : 'li',
25649             cls : 'nav-tab' + (qty ? '' : ' active'),
25650             cn : [
25651                 {
25652                     tag : 'a',
25653                     href:'#',
25654                     html : pane.title
25655                 }
25656             ]
25657         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25658         pane.tab = tab;
25659         
25660         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25661         if (!qty) {
25662             pane.el.addClass('active');
25663         }
25664         
25665                 
25666     },
25667     onTabClick : function(ev,un,ob,pane)
25668     {
25669         //Roo.log('tab - prev default');
25670         ev.preventDefault();
25671         
25672         
25673         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25674         pane.tab.addClass('active');
25675         //Roo.log(pane.title);
25676         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25677         // technically we should have a deactivate event.. but maybe add later.
25678         // and it should not de-activate the selected tab...
25679         this.fireEvent('activatepane', pane);
25680         pane.el.addClass('active');
25681         pane.fireEvent('activate');
25682         
25683         
25684     },
25685     
25686     getActivePane : function()
25687     {
25688         var r = false;
25689         Roo.each(this.panes, function(p) {
25690             if(p.el.hasClass('active')){
25691                 r = p;
25692                 return false;
25693             }
25694             
25695             return;
25696         });
25697         
25698         return r;
25699     }
25700     
25701     
25702 });
25703
25704  
25705 /*
25706  * - LGPL
25707  *
25708  * Tab pane
25709  * 
25710  */
25711 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25712 /**
25713  * @class Roo.bootstrap.TabPane
25714  * @extends Roo.bootstrap.Component
25715  * Bootstrap TabPane class
25716  * @cfg {Boolean} active (false | true) Default false
25717  * @cfg {String} title title of panel
25718
25719  * 
25720  * @constructor
25721  * Create a new TabPane
25722  * @param {Object} config The config object
25723  */
25724
25725 Roo.bootstrap.dash.TabPane = function(config){
25726     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25727     
25728     this.addEvents({
25729         // raw events
25730         /**
25731          * @event activate
25732          * When a pane is activated
25733          * @param {Roo.bootstrap.dash.TabPane} pane
25734          */
25735         "activate" : true
25736          
25737     });
25738 };
25739
25740 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25741     
25742     active : false,
25743     title : '',
25744     
25745     // the tabBox that this is attached to.
25746     tab : false,
25747      
25748     getAutoCreate : function() 
25749     {
25750         var cfg = {
25751             tag: 'div',
25752             cls: 'tab-pane'
25753         };
25754         
25755         if(this.active){
25756             cfg.cls += ' active';
25757         }
25758         
25759         return cfg;
25760     },
25761     initEvents  : function()
25762     {
25763         //Roo.log('trigger add pane handler');
25764         this.parent().fireEvent('addpane', this)
25765     },
25766     
25767      /**
25768      * Updates the tab title 
25769      * @param {String} html to set the title to.
25770      */
25771     setTitle: function(str)
25772     {
25773         if (!this.tab) {
25774             return;
25775         }
25776         this.title = str;
25777         this.tab.select('a', true).first().dom.innerHTML = str;
25778         
25779     }
25780     
25781     
25782     
25783 });
25784
25785  
25786
25787
25788  /*
25789  * - LGPL
25790  *
25791  * menu
25792  * 
25793  */
25794 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25795
25796 /**
25797  * @class Roo.bootstrap.menu.Menu
25798  * @extends Roo.bootstrap.Component
25799  * Bootstrap Menu class - container for Menu
25800  * @cfg {String} html Text of the menu
25801  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25802  * @cfg {String} icon Font awesome icon
25803  * @cfg {String} pos Menu align to (top | bottom) default bottom
25804  * 
25805  * 
25806  * @constructor
25807  * Create a new Menu
25808  * @param {Object} config The config object
25809  */
25810
25811
25812 Roo.bootstrap.menu.Menu = function(config){
25813     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25814     
25815     this.addEvents({
25816         /**
25817          * @event beforeshow
25818          * Fires before this menu is displayed
25819          * @param {Roo.bootstrap.menu.Menu} this
25820          */
25821         beforeshow : true,
25822         /**
25823          * @event beforehide
25824          * Fires before this menu is hidden
25825          * @param {Roo.bootstrap.menu.Menu} this
25826          */
25827         beforehide : true,
25828         /**
25829          * @event show
25830          * Fires after this menu is displayed
25831          * @param {Roo.bootstrap.menu.Menu} this
25832          */
25833         show : true,
25834         /**
25835          * @event hide
25836          * Fires after this menu is hidden
25837          * @param {Roo.bootstrap.menu.Menu} this
25838          */
25839         hide : true,
25840         /**
25841          * @event click
25842          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25843          * @param {Roo.bootstrap.menu.Menu} this
25844          * @param {Roo.EventObject} e
25845          */
25846         click : true
25847     });
25848     
25849 };
25850
25851 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25852     
25853     submenu : false,
25854     html : '',
25855     weight : 'default',
25856     icon : false,
25857     pos : 'bottom',
25858     
25859     
25860     getChildContainer : function() {
25861         if(this.isSubMenu){
25862             return this.el;
25863         }
25864         
25865         return this.el.select('ul.dropdown-menu', true).first();  
25866     },
25867     
25868     getAutoCreate : function()
25869     {
25870         var text = [
25871             {
25872                 tag : 'span',
25873                 cls : 'roo-menu-text',
25874                 html : this.html
25875             }
25876         ];
25877         
25878         if(this.icon){
25879             text.unshift({
25880                 tag : 'i',
25881                 cls : 'fa ' + this.icon
25882             })
25883         }
25884         
25885         
25886         var cfg = {
25887             tag : 'div',
25888             cls : 'btn-group',
25889             cn : [
25890                 {
25891                     tag : 'button',
25892                     cls : 'dropdown-button btn btn-' + this.weight,
25893                     cn : text
25894                 },
25895                 {
25896                     tag : 'button',
25897                     cls : 'dropdown-toggle btn btn-' + this.weight,
25898                     cn : [
25899                         {
25900                             tag : 'span',
25901                             cls : 'caret'
25902                         }
25903                     ]
25904                 },
25905                 {
25906                     tag : 'ul',
25907                     cls : 'dropdown-menu'
25908                 }
25909             ]
25910             
25911         };
25912         
25913         if(this.pos == 'top'){
25914             cfg.cls += ' dropup';
25915         }
25916         
25917         if(this.isSubMenu){
25918             cfg = {
25919                 tag : 'ul',
25920                 cls : 'dropdown-menu'
25921             }
25922         }
25923         
25924         return cfg;
25925     },
25926     
25927     onRender : function(ct, position)
25928     {
25929         this.isSubMenu = ct.hasClass('dropdown-submenu');
25930         
25931         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25932     },
25933     
25934     initEvents : function() 
25935     {
25936         if(this.isSubMenu){
25937             return;
25938         }
25939         
25940         this.hidden = true;
25941         
25942         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25943         this.triggerEl.on('click', this.onTriggerPress, this);
25944         
25945         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25946         this.buttonEl.on('click', this.onClick, this);
25947         
25948     },
25949     
25950     list : function()
25951     {
25952         if(this.isSubMenu){
25953             return this.el;
25954         }
25955         
25956         return this.el.select('ul.dropdown-menu', true).first();
25957     },
25958     
25959     onClick : function(e)
25960     {
25961         this.fireEvent("click", this, e);
25962     },
25963     
25964     onTriggerPress  : function(e)
25965     {   
25966         if (this.isVisible()) {
25967             this.hide();
25968         } else {
25969             this.show();
25970         }
25971     },
25972     
25973     isVisible : function(){
25974         return !this.hidden;
25975     },
25976     
25977     show : function()
25978     {
25979         this.fireEvent("beforeshow", this);
25980         
25981         this.hidden = false;
25982         this.el.addClass('open');
25983         
25984         Roo.get(document).on("mouseup", this.onMouseUp, this);
25985         
25986         this.fireEvent("show", this);
25987         
25988         
25989     },
25990     
25991     hide : function()
25992     {
25993         this.fireEvent("beforehide", this);
25994         
25995         this.hidden = true;
25996         this.el.removeClass('open');
25997         
25998         Roo.get(document).un("mouseup", this.onMouseUp);
25999         
26000         this.fireEvent("hide", this);
26001     },
26002     
26003     onMouseUp : function()
26004     {
26005         this.hide();
26006     }
26007     
26008 });
26009
26010  
26011  /*
26012  * - LGPL
26013  *
26014  * menu item
26015  * 
26016  */
26017 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26018
26019 /**
26020  * @class Roo.bootstrap.menu.Item
26021  * @extends Roo.bootstrap.Component
26022  * Bootstrap MenuItem class
26023  * @cfg {Boolean} submenu (true | false) default false
26024  * @cfg {String} html text of the item
26025  * @cfg {String} href the link
26026  * @cfg {Boolean} disable (true | false) default false
26027  * @cfg {Boolean} preventDefault (true | false) default true
26028  * @cfg {String} icon Font awesome icon
26029  * @cfg {String} pos Submenu align to (left | right) default right 
26030  * 
26031  * 
26032  * @constructor
26033  * Create a new Item
26034  * @param {Object} config The config object
26035  */
26036
26037
26038 Roo.bootstrap.menu.Item = function(config){
26039     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26040     this.addEvents({
26041         /**
26042          * @event mouseover
26043          * Fires when the mouse is hovering over this menu
26044          * @param {Roo.bootstrap.menu.Item} this
26045          * @param {Roo.EventObject} e
26046          */
26047         mouseover : true,
26048         /**
26049          * @event mouseout
26050          * Fires when the mouse exits this menu
26051          * @param {Roo.bootstrap.menu.Item} this
26052          * @param {Roo.EventObject} e
26053          */
26054         mouseout : true,
26055         // raw events
26056         /**
26057          * @event click
26058          * The raw click event for the entire grid.
26059          * @param {Roo.EventObject} e
26060          */
26061         click : true
26062     });
26063 };
26064
26065 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26066     
26067     submenu : false,
26068     href : '',
26069     html : '',
26070     preventDefault: true,
26071     disable : false,
26072     icon : false,
26073     pos : 'right',
26074     
26075     getAutoCreate : function()
26076     {
26077         var text = [
26078             {
26079                 tag : 'span',
26080                 cls : 'roo-menu-item-text',
26081                 html : this.html
26082             }
26083         ];
26084         
26085         if(this.icon){
26086             text.unshift({
26087                 tag : 'i',
26088                 cls : 'fa ' + this.icon
26089             })
26090         }
26091         
26092         var cfg = {
26093             tag : 'li',
26094             cn : [
26095                 {
26096                     tag : 'a',
26097                     href : this.href || '#',
26098                     cn : text
26099                 }
26100             ]
26101         };
26102         
26103         if(this.disable){
26104             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26105         }
26106         
26107         if(this.submenu){
26108             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26109             
26110             if(this.pos == 'left'){
26111                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26112             }
26113         }
26114         
26115         return cfg;
26116     },
26117     
26118     initEvents : function() 
26119     {
26120         this.el.on('mouseover', this.onMouseOver, this);
26121         this.el.on('mouseout', this.onMouseOut, this);
26122         
26123         this.el.select('a', true).first().on('click', this.onClick, this);
26124         
26125     },
26126     
26127     onClick : function(e)
26128     {
26129         if(this.preventDefault){
26130             e.preventDefault();
26131         }
26132         
26133         this.fireEvent("click", this, e);
26134     },
26135     
26136     onMouseOver : function(e)
26137     {
26138         if(this.submenu && this.pos == 'left'){
26139             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26140         }
26141         
26142         this.fireEvent("mouseover", this, e);
26143     },
26144     
26145     onMouseOut : function(e)
26146     {
26147         this.fireEvent("mouseout", this, e);
26148     }
26149 });
26150
26151  
26152
26153  /*
26154  * - LGPL
26155  *
26156  * menu separator
26157  * 
26158  */
26159 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26160
26161 /**
26162  * @class Roo.bootstrap.menu.Separator
26163  * @extends Roo.bootstrap.Component
26164  * Bootstrap Separator class
26165  * 
26166  * @constructor
26167  * Create a new Separator
26168  * @param {Object} config The config object
26169  */
26170
26171
26172 Roo.bootstrap.menu.Separator = function(config){
26173     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26174 };
26175
26176 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26177     
26178     getAutoCreate : function(){
26179         var cfg = {
26180             tag : 'li',
26181             cls: 'divider'
26182         };
26183         
26184         return cfg;
26185     }
26186    
26187 });
26188
26189  
26190
26191  /*
26192  * - LGPL
26193  *
26194  * Tooltip
26195  * 
26196  */
26197
26198 /**
26199  * @class Roo.bootstrap.Tooltip
26200  * Bootstrap Tooltip class
26201  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26202  * to determine which dom element triggers the tooltip.
26203  * 
26204  * It needs to add support for additional attributes like tooltip-position
26205  * 
26206  * @constructor
26207  * Create a new Toolti
26208  * @param {Object} config The config object
26209  */
26210
26211 Roo.bootstrap.Tooltip = function(config){
26212     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26213     
26214     this.alignment = Roo.bootstrap.Tooltip.alignment;
26215     
26216     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26217         this.alignment = config.alignment;
26218     }
26219     
26220 };
26221
26222 Roo.apply(Roo.bootstrap.Tooltip, {
26223     /**
26224      * @function init initialize tooltip monitoring.
26225      * @static
26226      */
26227     currentEl : false,
26228     currentTip : false,
26229     currentRegion : false,
26230     
26231     //  init : delay?
26232     
26233     init : function()
26234     {
26235         Roo.get(document).on('mouseover', this.enter ,this);
26236         Roo.get(document).on('mouseout', this.leave, this);
26237          
26238         
26239         this.currentTip = new Roo.bootstrap.Tooltip();
26240     },
26241     
26242     enter : function(ev)
26243     {
26244         var dom = ev.getTarget();
26245         
26246         //Roo.log(['enter',dom]);
26247         var el = Roo.fly(dom);
26248         if (this.currentEl) {
26249             //Roo.log(dom);
26250             //Roo.log(this.currentEl);
26251             //Roo.log(this.currentEl.contains(dom));
26252             if (this.currentEl == el) {
26253                 return;
26254             }
26255             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26256                 return;
26257             }
26258
26259         }
26260         
26261         if (this.currentTip.el) {
26262             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26263         }    
26264         //Roo.log(ev);
26265         
26266         if(!el || el.dom == document){
26267             return;
26268         }
26269         
26270         var bindEl = el;
26271         
26272         // you can not look for children, as if el is the body.. then everythign is the child..
26273         if (!el.attr('tooltip')) { //
26274             if (!el.select("[tooltip]").elements.length) {
26275                 return;
26276             }
26277             // is the mouse over this child...?
26278             bindEl = el.select("[tooltip]").first();
26279             var xy = ev.getXY();
26280             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26281                 //Roo.log("not in region.");
26282                 return;
26283             }
26284             //Roo.log("child element over..");
26285             
26286         }
26287         this.currentEl = bindEl;
26288         this.currentTip.bind(bindEl);
26289         this.currentRegion = Roo.lib.Region.getRegion(dom);
26290         this.currentTip.enter();
26291         
26292     },
26293     leave : function(ev)
26294     {
26295         var dom = ev.getTarget();
26296         //Roo.log(['leave',dom]);
26297         if (!this.currentEl) {
26298             return;
26299         }
26300         
26301         
26302         if (dom != this.currentEl.dom) {
26303             return;
26304         }
26305         var xy = ev.getXY();
26306         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26307             return;
26308         }
26309         // only activate leave if mouse cursor is outside... bounding box..
26310         
26311         
26312         
26313         
26314         if (this.currentTip) {
26315             this.currentTip.leave();
26316         }
26317         //Roo.log('clear currentEl');
26318         this.currentEl = false;
26319         
26320         
26321     },
26322     alignment : {
26323         'left' : ['r-l', [-2,0], 'right'],
26324         'right' : ['l-r', [2,0], 'left'],
26325         'bottom' : ['t-b', [0,2], 'top'],
26326         'top' : [ 'b-t', [0,-2], 'bottom']
26327     }
26328     
26329 });
26330
26331
26332 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26333     
26334     
26335     bindEl : false,
26336     
26337     delay : null, // can be { show : 300 , hide: 500}
26338     
26339     timeout : null,
26340     
26341     hoverState : null, //???
26342     
26343     placement : 'bottom', 
26344     
26345     alignment : false,
26346     
26347     getAutoCreate : function(){
26348     
26349         var cfg = {
26350            cls : 'tooltip',
26351            role : 'tooltip',
26352            cn : [
26353                 {
26354                     cls : 'tooltip-arrow'
26355                 },
26356                 {
26357                     cls : 'tooltip-inner'
26358                 }
26359            ]
26360         };
26361         
26362         return cfg;
26363     },
26364     bind : function(el)
26365     {
26366         this.bindEl = el;
26367     },
26368       
26369     
26370     enter : function () {
26371        
26372         if (this.timeout != null) {
26373             clearTimeout(this.timeout);
26374         }
26375         
26376         this.hoverState = 'in';
26377          //Roo.log("enter - show");
26378         if (!this.delay || !this.delay.show) {
26379             this.show();
26380             return;
26381         }
26382         var _t = this;
26383         this.timeout = setTimeout(function () {
26384             if (_t.hoverState == 'in') {
26385                 _t.show();
26386             }
26387         }, this.delay.show);
26388     },
26389     leave : function()
26390     {
26391         clearTimeout(this.timeout);
26392     
26393         this.hoverState = 'out';
26394          if (!this.delay || !this.delay.hide) {
26395             this.hide();
26396             return;
26397         }
26398        
26399         var _t = this;
26400         this.timeout = setTimeout(function () {
26401             //Roo.log("leave - timeout");
26402             
26403             if (_t.hoverState == 'out') {
26404                 _t.hide();
26405                 Roo.bootstrap.Tooltip.currentEl = false;
26406             }
26407         }, delay);
26408     },
26409     
26410     show : function (msg)
26411     {
26412         if (!this.el) {
26413             this.render(document.body);
26414         }
26415         // set content.
26416         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26417         
26418         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26419         
26420         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26421         
26422         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26423         
26424         var placement = typeof this.placement == 'function' ?
26425             this.placement.call(this, this.el, on_el) :
26426             this.placement;
26427             
26428         var autoToken = /\s?auto?\s?/i;
26429         var autoPlace = autoToken.test(placement);
26430         if (autoPlace) {
26431             placement = placement.replace(autoToken, '') || 'top';
26432         }
26433         
26434         //this.el.detach()
26435         //this.el.setXY([0,0]);
26436         this.el.show();
26437         //this.el.dom.style.display='block';
26438         
26439         //this.el.appendTo(on_el);
26440         
26441         var p = this.getPosition();
26442         var box = this.el.getBox();
26443         
26444         if (autoPlace) {
26445             // fixme..
26446         }
26447         
26448         var align = this.alignment[placement];
26449         
26450         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26451         
26452         if(placement == 'top' || placement == 'bottom'){
26453             if(xy[0] < 0){
26454                 placement = 'right';
26455             }
26456             
26457             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26458                 placement = 'left';
26459             }
26460             
26461             var scroll = Roo.select('body', true).first().getScroll();
26462             
26463             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26464                 placement = 'top';
26465             }
26466             
26467             align = this.alignment[placement];
26468         }
26469         
26470         this.el.alignTo(this.bindEl, align[0],align[1]);
26471         //var arrow = this.el.select('.arrow',true).first();
26472         //arrow.set(align[2], 
26473         
26474         this.el.addClass(placement);
26475         
26476         this.el.addClass('in fade');
26477         
26478         this.hoverState = null;
26479         
26480         if (this.el.hasClass('fade')) {
26481             // fade it?
26482         }
26483         
26484     },
26485     hide : function()
26486     {
26487          
26488         if (!this.el) {
26489             return;
26490         }
26491         //this.el.setXY([0,0]);
26492         this.el.removeClass('in');
26493         //this.el.hide();
26494         
26495     }
26496     
26497 });
26498  
26499
26500  /*
26501  * - LGPL
26502  *
26503  * Location Picker
26504  * 
26505  */
26506
26507 /**
26508  * @class Roo.bootstrap.LocationPicker
26509  * @extends Roo.bootstrap.Component
26510  * Bootstrap LocationPicker class
26511  * @cfg {Number} latitude Position when init default 0
26512  * @cfg {Number} longitude Position when init default 0
26513  * @cfg {Number} zoom default 15
26514  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26515  * @cfg {Boolean} mapTypeControl default false
26516  * @cfg {Boolean} disableDoubleClickZoom default false
26517  * @cfg {Boolean} scrollwheel default true
26518  * @cfg {Boolean} streetViewControl default false
26519  * @cfg {Number} radius default 0
26520  * @cfg {String} locationName
26521  * @cfg {Boolean} draggable default true
26522  * @cfg {Boolean} enableAutocomplete default false
26523  * @cfg {Boolean} enableReverseGeocode default true
26524  * @cfg {String} markerTitle
26525  * 
26526  * @constructor
26527  * Create a new LocationPicker
26528  * @param {Object} config The config object
26529  */
26530
26531
26532 Roo.bootstrap.LocationPicker = function(config){
26533     
26534     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26535     
26536     this.addEvents({
26537         /**
26538          * @event initial
26539          * Fires when the picker initialized.
26540          * @param {Roo.bootstrap.LocationPicker} this
26541          * @param {Google Location} location
26542          */
26543         initial : true,
26544         /**
26545          * @event positionchanged
26546          * Fires when the picker position changed.
26547          * @param {Roo.bootstrap.LocationPicker} this
26548          * @param {Google Location} location
26549          */
26550         positionchanged : true,
26551         /**
26552          * @event resize
26553          * Fires when the map resize.
26554          * @param {Roo.bootstrap.LocationPicker} this
26555          */
26556         resize : true,
26557         /**
26558          * @event show
26559          * Fires when the map show.
26560          * @param {Roo.bootstrap.LocationPicker} this
26561          */
26562         show : true,
26563         /**
26564          * @event hide
26565          * Fires when the map hide.
26566          * @param {Roo.bootstrap.LocationPicker} this
26567          */
26568         hide : true,
26569         /**
26570          * @event mapClick
26571          * Fires when click the map.
26572          * @param {Roo.bootstrap.LocationPicker} this
26573          * @param {Map event} e
26574          */
26575         mapClick : true,
26576         /**
26577          * @event mapRightClick
26578          * Fires when right click the map.
26579          * @param {Roo.bootstrap.LocationPicker} this
26580          * @param {Map event} e
26581          */
26582         mapRightClick : true,
26583         /**
26584          * @event markerClick
26585          * Fires when click the marker.
26586          * @param {Roo.bootstrap.LocationPicker} this
26587          * @param {Map event} e
26588          */
26589         markerClick : true,
26590         /**
26591          * @event markerRightClick
26592          * Fires when right click the marker.
26593          * @param {Roo.bootstrap.LocationPicker} this
26594          * @param {Map event} e
26595          */
26596         markerRightClick : true,
26597         /**
26598          * @event OverlayViewDraw
26599          * Fires when OverlayView Draw
26600          * @param {Roo.bootstrap.LocationPicker} this
26601          */
26602         OverlayViewDraw : true,
26603         /**
26604          * @event OverlayViewOnAdd
26605          * Fires when OverlayView Draw
26606          * @param {Roo.bootstrap.LocationPicker} this
26607          */
26608         OverlayViewOnAdd : true,
26609         /**
26610          * @event OverlayViewOnRemove
26611          * Fires when OverlayView Draw
26612          * @param {Roo.bootstrap.LocationPicker} this
26613          */
26614         OverlayViewOnRemove : true,
26615         /**
26616          * @event OverlayViewShow
26617          * Fires when OverlayView Draw
26618          * @param {Roo.bootstrap.LocationPicker} this
26619          * @param {Pixel} cpx
26620          */
26621         OverlayViewShow : true,
26622         /**
26623          * @event OverlayViewHide
26624          * Fires when OverlayView Draw
26625          * @param {Roo.bootstrap.LocationPicker} this
26626          */
26627         OverlayViewHide : true,
26628         /**
26629          * @event loadexception
26630          * Fires when load google lib failed.
26631          * @param {Roo.bootstrap.LocationPicker} this
26632          */
26633         loadexception : true
26634     });
26635         
26636 };
26637
26638 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26639     
26640     gMapContext: false,
26641     
26642     latitude: 0,
26643     longitude: 0,
26644     zoom: 15,
26645     mapTypeId: false,
26646     mapTypeControl: false,
26647     disableDoubleClickZoom: false,
26648     scrollwheel: true,
26649     streetViewControl: false,
26650     radius: 0,
26651     locationName: '',
26652     draggable: true,
26653     enableAutocomplete: false,
26654     enableReverseGeocode: true,
26655     markerTitle: '',
26656     
26657     getAutoCreate: function()
26658     {
26659
26660         var cfg = {
26661             tag: 'div',
26662             cls: 'roo-location-picker'
26663         };
26664         
26665         return cfg
26666     },
26667     
26668     initEvents: function(ct, position)
26669     {       
26670         if(!this.el.getWidth() || this.isApplied()){
26671             return;
26672         }
26673         
26674         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26675         
26676         this.initial();
26677     },
26678     
26679     initial: function()
26680     {
26681         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26682             this.fireEvent('loadexception', this);
26683             return;
26684         }
26685         
26686         if(!this.mapTypeId){
26687             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26688         }
26689         
26690         this.gMapContext = this.GMapContext();
26691         
26692         this.initOverlayView();
26693         
26694         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26695         
26696         var _this = this;
26697                 
26698         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26699             _this.setPosition(_this.gMapContext.marker.position);
26700         });
26701         
26702         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26703             _this.fireEvent('mapClick', this, event);
26704             
26705         });
26706
26707         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26708             _this.fireEvent('mapRightClick', this, event);
26709             
26710         });
26711         
26712         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26713             _this.fireEvent('markerClick', this, event);
26714             
26715         });
26716
26717         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26718             _this.fireEvent('markerRightClick', this, event);
26719             
26720         });
26721         
26722         this.setPosition(this.gMapContext.location);
26723         
26724         this.fireEvent('initial', this, this.gMapContext.location);
26725     },
26726     
26727     initOverlayView: function()
26728     {
26729         var _this = this;
26730         
26731         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26732             
26733             draw: function()
26734             {
26735                 _this.fireEvent('OverlayViewDraw', _this);
26736             },
26737             
26738             onAdd: function()
26739             {
26740                 _this.fireEvent('OverlayViewOnAdd', _this);
26741             },
26742             
26743             onRemove: function()
26744             {
26745                 _this.fireEvent('OverlayViewOnRemove', _this);
26746             },
26747             
26748             show: function(cpx)
26749             {
26750                 _this.fireEvent('OverlayViewShow', _this, cpx);
26751             },
26752             
26753             hide: function()
26754             {
26755                 _this.fireEvent('OverlayViewHide', _this);
26756             }
26757             
26758         });
26759     },
26760     
26761     fromLatLngToContainerPixel: function(event)
26762     {
26763         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26764     },
26765     
26766     isApplied: function() 
26767     {
26768         return this.getGmapContext() == false ? false : true;
26769     },
26770     
26771     getGmapContext: function() 
26772     {
26773         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26774     },
26775     
26776     GMapContext: function() 
26777     {
26778         var position = new google.maps.LatLng(this.latitude, this.longitude);
26779         
26780         var _map = new google.maps.Map(this.el.dom, {
26781             center: position,
26782             zoom: this.zoom,
26783             mapTypeId: this.mapTypeId,
26784             mapTypeControl: this.mapTypeControl,
26785             disableDoubleClickZoom: this.disableDoubleClickZoom,
26786             scrollwheel: this.scrollwheel,
26787             streetViewControl: this.streetViewControl,
26788             locationName: this.locationName,
26789             draggable: this.draggable,
26790             enableAutocomplete: this.enableAutocomplete,
26791             enableReverseGeocode: this.enableReverseGeocode
26792         });
26793         
26794         var _marker = new google.maps.Marker({
26795             position: position,
26796             map: _map,
26797             title: this.markerTitle,
26798             draggable: this.draggable
26799         });
26800         
26801         return {
26802             map: _map,
26803             marker: _marker,
26804             circle: null,
26805             location: position,
26806             radius: this.radius,
26807             locationName: this.locationName,
26808             addressComponents: {
26809                 formatted_address: null,
26810                 addressLine1: null,
26811                 addressLine2: null,
26812                 streetName: null,
26813                 streetNumber: null,
26814                 city: null,
26815                 district: null,
26816                 state: null,
26817                 stateOrProvince: null
26818             },
26819             settings: this,
26820             domContainer: this.el.dom,
26821             geodecoder: new google.maps.Geocoder()
26822         };
26823     },
26824     
26825     drawCircle: function(center, radius, options) 
26826     {
26827         if (this.gMapContext.circle != null) {
26828             this.gMapContext.circle.setMap(null);
26829         }
26830         if (radius > 0) {
26831             radius *= 1;
26832             options = Roo.apply({}, options, {
26833                 strokeColor: "#0000FF",
26834                 strokeOpacity: .35,
26835                 strokeWeight: 2,
26836                 fillColor: "#0000FF",
26837                 fillOpacity: .2
26838             });
26839             
26840             options.map = this.gMapContext.map;
26841             options.radius = radius;
26842             options.center = center;
26843             this.gMapContext.circle = new google.maps.Circle(options);
26844             return this.gMapContext.circle;
26845         }
26846         
26847         return null;
26848     },
26849     
26850     setPosition: function(location) 
26851     {
26852         this.gMapContext.location = location;
26853         this.gMapContext.marker.setPosition(location);
26854         this.gMapContext.map.panTo(location);
26855         this.drawCircle(location, this.gMapContext.radius, {});
26856         
26857         var _this = this;
26858         
26859         if (this.gMapContext.settings.enableReverseGeocode) {
26860             this.gMapContext.geodecoder.geocode({
26861                 latLng: this.gMapContext.location
26862             }, function(results, status) {
26863                 
26864                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26865                     _this.gMapContext.locationName = results[0].formatted_address;
26866                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26867                     
26868                     _this.fireEvent('positionchanged', this, location);
26869                 }
26870             });
26871             
26872             return;
26873         }
26874         
26875         this.fireEvent('positionchanged', this, location);
26876     },
26877     
26878     resize: function()
26879     {
26880         google.maps.event.trigger(this.gMapContext.map, "resize");
26881         
26882         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26883         
26884         this.fireEvent('resize', this);
26885     },
26886     
26887     setPositionByLatLng: function(latitude, longitude)
26888     {
26889         this.setPosition(new google.maps.LatLng(latitude, longitude));
26890     },
26891     
26892     getCurrentPosition: function() 
26893     {
26894         return {
26895             latitude: this.gMapContext.location.lat(),
26896             longitude: this.gMapContext.location.lng()
26897         };
26898     },
26899     
26900     getAddressName: function() 
26901     {
26902         return this.gMapContext.locationName;
26903     },
26904     
26905     getAddressComponents: function() 
26906     {
26907         return this.gMapContext.addressComponents;
26908     },
26909     
26910     address_component_from_google_geocode: function(address_components) 
26911     {
26912         var result = {};
26913         
26914         for (var i = 0; i < address_components.length; i++) {
26915             var component = address_components[i];
26916             if (component.types.indexOf("postal_code") >= 0) {
26917                 result.postalCode = component.short_name;
26918             } else if (component.types.indexOf("street_number") >= 0) {
26919                 result.streetNumber = component.short_name;
26920             } else if (component.types.indexOf("route") >= 0) {
26921                 result.streetName = component.short_name;
26922             } else if (component.types.indexOf("neighborhood") >= 0) {
26923                 result.city = component.short_name;
26924             } else if (component.types.indexOf("locality") >= 0) {
26925                 result.city = component.short_name;
26926             } else if (component.types.indexOf("sublocality") >= 0) {
26927                 result.district = component.short_name;
26928             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26929                 result.stateOrProvince = component.short_name;
26930             } else if (component.types.indexOf("country") >= 0) {
26931                 result.country = component.short_name;
26932             }
26933         }
26934         
26935         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26936         result.addressLine2 = "";
26937         return result;
26938     },
26939     
26940     setZoomLevel: function(zoom)
26941     {
26942         this.gMapContext.map.setZoom(zoom);
26943     },
26944     
26945     show: function()
26946     {
26947         if(!this.el){
26948             return;
26949         }
26950         
26951         this.el.show();
26952         
26953         this.resize();
26954         
26955         this.fireEvent('show', this);
26956     },
26957     
26958     hide: function()
26959     {
26960         if(!this.el){
26961             return;
26962         }
26963         
26964         this.el.hide();
26965         
26966         this.fireEvent('hide', this);
26967     }
26968     
26969 });
26970
26971 Roo.apply(Roo.bootstrap.LocationPicker, {
26972     
26973     OverlayView : function(map, options)
26974     {
26975         options = options || {};
26976         
26977         this.setMap(map);
26978     }
26979     
26980     
26981 });/*
26982  * - LGPL
26983  *
26984  * Alert
26985  * 
26986  */
26987
26988 /**
26989  * @class Roo.bootstrap.Alert
26990  * @extends Roo.bootstrap.Component
26991  * Bootstrap Alert class
26992  * @cfg {String} title The title of alert
26993  * @cfg {String} html The content of alert
26994  * @cfg {String} weight (  success | info | warning | danger )
26995  * @cfg {String} faicon font-awesomeicon
26996  * 
26997  * @constructor
26998  * Create a new alert
26999  * @param {Object} config The config object
27000  */
27001
27002
27003 Roo.bootstrap.Alert = function(config){
27004     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27005     
27006 };
27007
27008 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27009     
27010     title: '',
27011     html: '',
27012     weight: false,
27013     faicon: false,
27014     
27015     getAutoCreate : function()
27016     {
27017         
27018         var cfg = {
27019             tag : 'div',
27020             cls : 'alert',
27021             cn : [
27022                 {
27023                     tag : 'i',
27024                     cls : 'roo-alert-icon'
27025                     
27026                 },
27027                 {
27028                     tag : 'b',
27029                     cls : 'roo-alert-title',
27030                     html : this.title
27031                 },
27032                 {
27033                     tag : 'span',
27034                     cls : 'roo-alert-text',
27035                     html : this.html
27036                 }
27037             ]
27038         };
27039         
27040         if(this.faicon){
27041             cfg.cn[0].cls += ' fa ' + this.faicon;
27042         }
27043         
27044         if(this.weight){
27045             cfg.cls += ' alert-' + this.weight;
27046         }
27047         
27048         return cfg;
27049     },
27050     
27051     initEvents: function() 
27052     {
27053         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27054     },
27055     
27056     setTitle : function(str)
27057     {
27058         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27059     },
27060     
27061     setText : function(str)
27062     {
27063         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27064     },
27065     
27066     setWeight : function(weight)
27067     {
27068         if(this.weight){
27069             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27070         }
27071         
27072         this.weight = weight;
27073         
27074         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27075     },
27076     
27077     setIcon : function(icon)
27078     {
27079         if(this.faicon){
27080             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27081         }
27082         
27083         this.faicon = icon;
27084         
27085         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27086     },
27087     
27088     hide: function() 
27089     {
27090         this.el.hide();   
27091     },
27092     
27093     show: function() 
27094     {  
27095         this.el.show();   
27096     }
27097     
27098 });
27099
27100  
27101 /*
27102 * Licence: LGPL
27103 */
27104
27105 /**
27106  * @class Roo.bootstrap.UploadCropbox
27107  * @extends Roo.bootstrap.Component
27108  * Bootstrap UploadCropbox class
27109  * @cfg {String} emptyText show when image has been loaded
27110  * @cfg {String} rotateNotify show when image too small to rotate
27111  * @cfg {Number} errorTimeout default 3000
27112  * @cfg {Number} minWidth default 300
27113  * @cfg {Number} minHeight default 300
27114  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27115  * @cfg {Boolean} isDocument (true|false) default false
27116  * @cfg {String} url action url
27117  * @cfg {String} paramName default 'imageUpload'
27118  * @cfg {String} method default POST
27119  * @cfg {Boolean} loadMask (true|false) default true
27120  * @cfg {Boolean} loadingText default 'Loading...'
27121  * 
27122  * @constructor
27123  * Create a new UploadCropbox
27124  * @param {Object} config The config object
27125  */
27126
27127 Roo.bootstrap.UploadCropbox = function(config){
27128     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27129     
27130     this.addEvents({
27131         /**
27132          * @event beforeselectfile
27133          * Fire before select file
27134          * @param {Roo.bootstrap.UploadCropbox} this
27135          */
27136         "beforeselectfile" : true,
27137         /**
27138          * @event initial
27139          * Fire after initEvent
27140          * @param {Roo.bootstrap.UploadCropbox} this
27141          */
27142         "initial" : true,
27143         /**
27144          * @event crop
27145          * Fire after initEvent
27146          * @param {Roo.bootstrap.UploadCropbox} this
27147          * @param {String} data
27148          */
27149         "crop" : true,
27150         /**
27151          * @event prepare
27152          * Fire when preparing the file data
27153          * @param {Roo.bootstrap.UploadCropbox} this
27154          * @param {Object} file
27155          */
27156         "prepare" : true,
27157         /**
27158          * @event exception
27159          * Fire when get exception
27160          * @param {Roo.bootstrap.UploadCropbox} this
27161          * @param {XMLHttpRequest} xhr
27162          */
27163         "exception" : true,
27164         /**
27165          * @event beforeloadcanvas
27166          * Fire before load the canvas
27167          * @param {Roo.bootstrap.UploadCropbox} this
27168          * @param {String} src
27169          */
27170         "beforeloadcanvas" : true,
27171         /**
27172          * @event trash
27173          * Fire when trash image
27174          * @param {Roo.bootstrap.UploadCropbox} this
27175          */
27176         "trash" : true,
27177         /**
27178          * @event download
27179          * Fire when download the image
27180          * @param {Roo.bootstrap.UploadCropbox} this
27181          */
27182         "download" : true,
27183         /**
27184          * @event footerbuttonclick
27185          * Fire when footerbuttonclick
27186          * @param {Roo.bootstrap.UploadCropbox} this
27187          * @param {String} type
27188          */
27189         "footerbuttonclick" : true,
27190         /**
27191          * @event resize
27192          * Fire when resize
27193          * @param {Roo.bootstrap.UploadCropbox} this
27194          */
27195         "resize" : true,
27196         /**
27197          * @event rotate
27198          * Fire when rotate the image
27199          * @param {Roo.bootstrap.UploadCropbox} this
27200          * @param {String} pos
27201          */
27202         "rotate" : true,
27203         /**
27204          * @event inspect
27205          * Fire when inspect the file
27206          * @param {Roo.bootstrap.UploadCropbox} this
27207          * @param {Object} file
27208          */
27209         "inspect" : true,
27210         /**
27211          * @event upload
27212          * Fire when xhr upload the file
27213          * @param {Roo.bootstrap.UploadCropbox} this
27214          * @param {Object} data
27215          */
27216         "upload" : true,
27217         /**
27218          * @event arrange
27219          * Fire when arrange the file data
27220          * @param {Roo.bootstrap.UploadCropbox} this
27221          * @param {Object} formData
27222          */
27223         "arrange" : true
27224     });
27225     
27226     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27227 };
27228
27229 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27230     
27231     emptyText : 'Click to upload image',
27232     rotateNotify : 'Image is too small to rotate',
27233     errorTimeout : 3000,
27234     scale : 0,
27235     baseScale : 1,
27236     rotate : 0,
27237     dragable : false,
27238     pinching : false,
27239     mouseX : 0,
27240     mouseY : 0,
27241     cropData : false,
27242     minWidth : 300,
27243     minHeight : 300,
27244     file : false,
27245     exif : {},
27246     baseRotate : 1,
27247     cropType : 'image/jpeg',
27248     buttons : false,
27249     canvasLoaded : false,
27250     isDocument : false,
27251     method : 'POST',
27252     paramName : 'imageUpload',
27253     loadMask : true,
27254     loadingText : 'Loading...',
27255     maskEl : false,
27256     
27257     getAutoCreate : function()
27258     {
27259         var cfg = {
27260             tag : 'div',
27261             cls : 'roo-upload-cropbox',
27262             cn : [
27263                 {
27264                     tag : 'input',
27265                     cls : 'roo-upload-cropbox-selector',
27266                     type : 'file'
27267                 },
27268                 {
27269                     tag : 'div',
27270                     cls : 'roo-upload-cropbox-body',
27271                     style : 'cursor:pointer',
27272                     cn : [
27273                         {
27274                             tag : 'div',
27275                             cls : 'roo-upload-cropbox-preview'
27276                         },
27277                         {
27278                             tag : 'div',
27279                             cls : 'roo-upload-cropbox-thumb'
27280                         },
27281                         {
27282                             tag : 'div',
27283                             cls : 'roo-upload-cropbox-empty-notify',
27284                             html : this.emptyText
27285                         },
27286                         {
27287                             tag : 'div',
27288                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27289                             html : this.rotateNotify
27290                         }
27291                     ]
27292                 },
27293                 {
27294                     tag : 'div',
27295                     cls : 'roo-upload-cropbox-footer',
27296                     cn : {
27297                         tag : 'div',
27298                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27299                         cn : []
27300                     }
27301                 }
27302             ]
27303         };
27304         
27305         return cfg;
27306     },
27307     
27308     onRender : function(ct, position)
27309     {
27310         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27311         
27312         if (this.buttons.length) {
27313             
27314             Roo.each(this.buttons, function(bb) {
27315                 
27316                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27317                 
27318                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27319                 
27320             }, this);
27321         }
27322         
27323         if(this.loadMask){
27324             this.maskEl = this.el;
27325         }
27326     },
27327     
27328     initEvents : function()
27329     {
27330         this.urlAPI = (window.createObjectURL && window) || 
27331                                 (window.URL && URL.revokeObjectURL && URL) || 
27332                                 (window.webkitURL && webkitURL);
27333                         
27334         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27335         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27336         
27337         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27338         this.selectorEl.hide();
27339         
27340         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27341         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27342         
27343         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27344         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27345         this.thumbEl.hide();
27346         
27347         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27348         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27349         
27350         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27351         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27352         this.errorEl.hide();
27353         
27354         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27355         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27356         this.footerEl.hide();
27357         
27358         this.setThumbBoxSize();
27359         
27360         this.bind();
27361         
27362         this.resize();
27363         
27364         this.fireEvent('initial', this);
27365     },
27366
27367     bind : function()
27368     {
27369         var _this = this;
27370         
27371         window.addEventListener("resize", function() { _this.resize(); } );
27372         
27373         this.bodyEl.on('click', this.beforeSelectFile, this);
27374         
27375         if(Roo.isTouch){
27376             this.bodyEl.on('touchstart', this.onTouchStart, this);
27377             this.bodyEl.on('touchmove', this.onTouchMove, this);
27378             this.bodyEl.on('touchend', this.onTouchEnd, this);
27379         }
27380         
27381         if(!Roo.isTouch){
27382             this.bodyEl.on('mousedown', this.onMouseDown, this);
27383             this.bodyEl.on('mousemove', this.onMouseMove, this);
27384             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27385             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27386             Roo.get(document).on('mouseup', this.onMouseUp, this);
27387         }
27388         
27389         this.selectorEl.on('change', this.onFileSelected, this);
27390     },
27391     
27392     reset : function()
27393     {    
27394         this.scale = 0;
27395         this.baseScale = 1;
27396         this.rotate = 0;
27397         this.baseRotate = 1;
27398         this.dragable = false;
27399         this.pinching = false;
27400         this.mouseX = 0;
27401         this.mouseY = 0;
27402         this.cropData = false;
27403         this.notifyEl.dom.innerHTML = this.emptyText;
27404         
27405         this.selectorEl.dom.value = '';
27406         
27407     },
27408     
27409     resize : function()
27410     {
27411         if(this.fireEvent('resize', this) != false){
27412             this.setThumbBoxPosition();
27413             this.setCanvasPosition();
27414         }
27415     },
27416     
27417     onFooterButtonClick : function(e, el, o, type)
27418     {
27419         switch (type) {
27420             case 'rotate-left' :
27421                 this.onRotateLeft(e);
27422                 break;
27423             case 'rotate-right' :
27424                 this.onRotateRight(e);
27425                 break;
27426             case 'picture' :
27427                 this.beforeSelectFile(e);
27428                 break;
27429             case 'trash' :
27430                 this.trash(e);
27431                 break;
27432             case 'crop' :
27433                 this.crop(e);
27434                 break;
27435             case 'download' :
27436                 this.download(e);
27437                 break;
27438             default :
27439                 break;
27440         }
27441         
27442         this.fireEvent('footerbuttonclick', this, type);
27443     },
27444     
27445     beforeSelectFile : function(e)
27446     {
27447         e.preventDefault();
27448         
27449         if(this.fireEvent('beforeselectfile', this) != false){
27450             this.selectorEl.dom.click();
27451         }
27452     },
27453     
27454     onFileSelected : function(e)
27455     {
27456         e.preventDefault();
27457         
27458         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27459             return;
27460         }
27461         
27462         var file = this.selectorEl.dom.files[0];
27463         
27464         if(this.fireEvent('inspect', this, file) != false){
27465             this.prepare(file);
27466         }
27467         
27468     },
27469     
27470     trash : function(e)
27471     {
27472         this.fireEvent('trash', this);
27473     },
27474     
27475     download : function(e)
27476     {
27477         this.fireEvent('download', this);
27478     },
27479     
27480     loadCanvas : function(src)
27481     {   
27482         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27483             
27484             this.reset();
27485             
27486             this.imageEl = document.createElement('img');
27487             
27488             var _this = this;
27489             
27490             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27491             
27492             this.imageEl.src = src;
27493         }
27494     },
27495     
27496     onLoadCanvas : function()
27497     {   
27498         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27499         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27500         
27501         this.bodyEl.un('click', this.beforeSelectFile, this);
27502         
27503         this.notifyEl.hide();
27504         this.thumbEl.show();
27505         this.footerEl.show();
27506         
27507         this.baseRotateLevel();
27508         
27509         if(this.isDocument){
27510             this.setThumbBoxSize();
27511         }
27512         
27513         this.setThumbBoxPosition();
27514         
27515         this.baseScaleLevel();
27516         
27517         this.draw();
27518         
27519         this.resize();
27520         
27521         this.canvasLoaded = true;
27522         
27523         if(this.loadMask){
27524             this.maskEl.unmask();
27525         }
27526         
27527     },
27528     
27529     setCanvasPosition : function()
27530     {   
27531         if(!this.canvasEl){
27532             return;
27533         }
27534         
27535         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27536         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27537         
27538         this.previewEl.setLeft(pw);
27539         this.previewEl.setTop(ph);
27540         
27541     },
27542     
27543     onMouseDown : function(e)
27544     {   
27545         e.stopEvent();
27546         
27547         this.dragable = true;
27548         this.pinching = false;
27549         
27550         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27551             this.dragable = false;
27552             return;
27553         }
27554         
27555         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27556         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27557         
27558     },
27559     
27560     onMouseMove : function(e)
27561     {   
27562         e.stopEvent();
27563         
27564         if(!this.canvasLoaded){
27565             return;
27566         }
27567         
27568         if (!this.dragable){
27569             return;
27570         }
27571         
27572         var minX = Math.ceil(this.thumbEl.getLeft(true));
27573         var minY = Math.ceil(this.thumbEl.getTop(true));
27574         
27575         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27576         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27577         
27578         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27579         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27580         
27581         x = x - this.mouseX;
27582         y = y - this.mouseY;
27583         
27584         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27585         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27586         
27587         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27588         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27589         
27590         this.previewEl.setLeft(bgX);
27591         this.previewEl.setTop(bgY);
27592         
27593         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27594         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27595     },
27596     
27597     onMouseUp : function(e)
27598     {   
27599         e.stopEvent();
27600         
27601         this.dragable = false;
27602     },
27603     
27604     onMouseWheel : function(e)
27605     {   
27606         e.stopEvent();
27607         
27608         this.startScale = this.scale;
27609         
27610         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27611         
27612         if(!this.zoomable()){
27613             this.scale = this.startScale;
27614             return;
27615         }
27616         
27617         this.draw();
27618         
27619         return;
27620     },
27621     
27622     zoomable : function()
27623     {
27624         var minScale = this.thumbEl.getWidth() / this.minWidth;
27625         
27626         if(this.minWidth < this.minHeight){
27627             minScale = this.thumbEl.getHeight() / this.minHeight;
27628         }
27629         
27630         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27631         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27632         
27633         if(
27634                 this.isDocument &&
27635                 (this.rotate == 0 || this.rotate == 180) && 
27636                 (
27637                     width > this.imageEl.OriginWidth || 
27638                     height > this.imageEl.OriginHeight ||
27639                     (width < this.minWidth && height < this.minHeight)
27640                 )
27641         ){
27642             return false;
27643         }
27644         
27645         if(
27646                 this.isDocument &&
27647                 (this.rotate == 90 || this.rotate == 270) && 
27648                 (
27649                     width > this.imageEl.OriginWidth || 
27650                     height > this.imageEl.OriginHeight ||
27651                     (width < this.minHeight && height < this.minWidth)
27652                 )
27653         ){
27654             return false;
27655         }
27656         
27657         if(
27658                 !this.isDocument &&
27659                 (this.rotate == 0 || this.rotate == 180) && 
27660                 (
27661                     width < this.minWidth || 
27662                     width > this.imageEl.OriginWidth || 
27663                     height < this.minHeight || 
27664                     height > this.imageEl.OriginHeight
27665                 )
27666         ){
27667             return false;
27668         }
27669         
27670         if(
27671                 !this.isDocument &&
27672                 (this.rotate == 90 || this.rotate == 270) && 
27673                 (
27674                     width < this.minHeight || 
27675                     width > this.imageEl.OriginWidth || 
27676                     height < this.minWidth || 
27677                     height > this.imageEl.OriginHeight
27678                 )
27679         ){
27680             return false;
27681         }
27682         
27683         return true;
27684         
27685     },
27686     
27687     onRotateLeft : function(e)
27688     {   
27689         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27690             
27691             var minScale = this.thumbEl.getWidth() / this.minWidth;
27692             
27693             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27694             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27695             
27696             this.startScale = this.scale;
27697             
27698             while (this.getScaleLevel() < minScale){
27699             
27700                 this.scale = this.scale + 1;
27701                 
27702                 if(!this.zoomable()){
27703                     break;
27704                 }
27705                 
27706                 if(
27707                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27708                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27709                 ){
27710                     continue;
27711                 }
27712                 
27713                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27714
27715                 this.draw();
27716                 
27717                 return;
27718             }
27719             
27720             this.scale = this.startScale;
27721             
27722             this.onRotateFail();
27723             
27724             return false;
27725         }
27726         
27727         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27728
27729         if(this.isDocument){
27730             this.setThumbBoxSize();
27731             this.setThumbBoxPosition();
27732             this.setCanvasPosition();
27733         }
27734         
27735         this.draw();
27736         
27737         this.fireEvent('rotate', this, 'left');
27738         
27739     },
27740     
27741     onRotateRight : function(e)
27742     {
27743         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27744             
27745             var minScale = this.thumbEl.getWidth() / this.minWidth;
27746         
27747             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27748             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27749             
27750             this.startScale = this.scale;
27751             
27752             while (this.getScaleLevel() < minScale){
27753             
27754                 this.scale = this.scale + 1;
27755                 
27756                 if(!this.zoomable()){
27757                     break;
27758                 }
27759                 
27760                 if(
27761                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27762                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27763                 ){
27764                     continue;
27765                 }
27766                 
27767                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27768
27769                 this.draw();
27770                 
27771                 return;
27772             }
27773             
27774             this.scale = this.startScale;
27775             
27776             this.onRotateFail();
27777             
27778             return false;
27779         }
27780         
27781         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27782
27783         if(this.isDocument){
27784             this.setThumbBoxSize();
27785             this.setThumbBoxPosition();
27786             this.setCanvasPosition();
27787         }
27788         
27789         this.draw();
27790         
27791         this.fireEvent('rotate', this, 'right');
27792     },
27793     
27794     onRotateFail : function()
27795     {
27796         this.errorEl.show(true);
27797         
27798         var _this = this;
27799         
27800         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27801     },
27802     
27803     draw : function()
27804     {
27805         this.previewEl.dom.innerHTML = '';
27806         
27807         var canvasEl = document.createElement("canvas");
27808         
27809         var contextEl = canvasEl.getContext("2d");
27810         
27811         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27812         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27813         var center = this.imageEl.OriginWidth / 2;
27814         
27815         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27816             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27817             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27818             center = this.imageEl.OriginHeight / 2;
27819         }
27820         
27821         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27822         
27823         contextEl.translate(center, center);
27824         contextEl.rotate(this.rotate * Math.PI / 180);
27825
27826         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27827         
27828         this.canvasEl = document.createElement("canvas");
27829         
27830         this.contextEl = this.canvasEl.getContext("2d");
27831         
27832         switch (this.rotate) {
27833             case 0 :
27834                 
27835                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27836                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27837                 
27838                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27839                 
27840                 break;
27841             case 90 : 
27842                 
27843                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27844                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27845                 
27846                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27847                     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);
27848                     break;
27849                 }
27850                 
27851                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27852                 
27853                 break;
27854             case 180 :
27855                 
27856                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27857                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27858                 
27859                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27860                     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);
27861                     break;
27862                 }
27863                 
27864                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27865                 
27866                 break;
27867             case 270 :
27868                 
27869                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27870                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27871         
27872                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27873                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27874                     break;
27875                 }
27876                 
27877                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27878                 
27879                 break;
27880             default : 
27881                 break;
27882         }
27883         
27884         this.previewEl.appendChild(this.canvasEl);
27885         
27886         this.setCanvasPosition();
27887     },
27888     
27889     crop : function()
27890     {
27891         if(!this.canvasLoaded){
27892             return;
27893         }
27894         
27895         var imageCanvas = document.createElement("canvas");
27896         
27897         var imageContext = imageCanvas.getContext("2d");
27898         
27899         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27900         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27901         
27902         var center = imageCanvas.width / 2;
27903         
27904         imageContext.translate(center, center);
27905         
27906         imageContext.rotate(this.rotate * Math.PI / 180);
27907         
27908         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27909         
27910         var canvas = document.createElement("canvas");
27911         
27912         var context = canvas.getContext("2d");
27913                 
27914         canvas.width = this.minWidth;
27915         canvas.height = this.minHeight;
27916
27917         switch (this.rotate) {
27918             case 0 :
27919                 
27920                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27921                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27922                 
27923                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27924                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27925                 
27926                 var targetWidth = this.minWidth - 2 * x;
27927                 var targetHeight = this.minHeight - 2 * y;
27928                 
27929                 var scale = 1;
27930                 
27931                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27932                     scale = targetWidth / width;
27933                 }
27934                 
27935                 if(x > 0 && y == 0){
27936                     scale = targetHeight / height;
27937                 }
27938                 
27939                 if(x > 0 && y > 0){
27940                     scale = targetWidth / width;
27941                     
27942                     if(width < height){
27943                         scale = targetHeight / height;
27944                     }
27945                 }
27946                 
27947                 context.scale(scale, scale);
27948                 
27949                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27950                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27951
27952                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27953                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27954
27955                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27956                 
27957                 break;
27958             case 90 : 
27959                 
27960                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27961                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27962                 
27963                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27964                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27965                 
27966                 var targetWidth = this.minWidth - 2 * x;
27967                 var targetHeight = this.minHeight - 2 * y;
27968                 
27969                 var scale = 1;
27970                 
27971                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27972                     scale = targetWidth / width;
27973                 }
27974                 
27975                 if(x > 0 && y == 0){
27976                     scale = targetHeight / height;
27977                 }
27978                 
27979                 if(x > 0 && y > 0){
27980                     scale = targetWidth / width;
27981                     
27982                     if(width < height){
27983                         scale = targetHeight / height;
27984                     }
27985                 }
27986                 
27987                 context.scale(scale, scale);
27988                 
27989                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27990                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27991
27992                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27993                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27994                 
27995                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27996                 
27997                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27998                 
27999                 break;
28000             case 180 :
28001                 
28002                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28003                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28004                 
28005                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28006                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28007                 
28008                 var targetWidth = this.minWidth - 2 * x;
28009                 var targetHeight = this.minHeight - 2 * y;
28010                 
28011                 var scale = 1;
28012                 
28013                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28014                     scale = targetWidth / width;
28015                 }
28016                 
28017                 if(x > 0 && y == 0){
28018                     scale = targetHeight / height;
28019                 }
28020                 
28021                 if(x > 0 && y > 0){
28022                     scale = targetWidth / width;
28023                     
28024                     if(width < height){
28025                         scale = targetHeight / height;
28026                     }
28027                 }
28028                 
28029                 context.scale(scale, scale);
28030                 
28031                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28032                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28033
28034                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28035                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28036
28037                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28038                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28039                 
28040                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28041                 
28042                 break;
28043             case 270 :
28044                 
28045                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28046                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28047                 
28048                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28049                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28050                 
28051                 var targetWidth = this.minWidth - 2 * x;
28052                 var targetHeight = this.minHeight - 2 * y;
28053                 
28054                 var scale = 1;
28055                 
28056                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28057                     scale = targetWidth / width;
28058                 }
28059                 
28060                 if(x > 0 && y == 0){
28061                     scale = targetHeight / height;
28062                 }
28063                 
28064                 if(x > 0 && y > 0){
28065                     scale = targetWidth / width;
28066                     
28067                     if(width < height){
28068                         scale = targetHeight / height;
28069                     }
28070                 }
28071                 
28072                 context.scale(scale, scale);
28073                 
28074                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28075                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28076
28077                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28078                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28079                 
28080                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28081                 
28082                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28083                 
28084                 break;
28085             default : 
28086                 break;
28087         }
28088         
28089         this.cropData = canvas.toDataURL(this.cropType);
28090         
28091         if(this.fireEvent('crop', this, this.cropData) !== false){
28092             this.process(this.file, this.cropData);
28093         }
28094         
28095         return;
28096         
28097     },
28098     
28099     setThumbBoxSize : function()
28100     {
28101         var width, height;
28102         
28103         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28104             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28105             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28106             
28107             this.minWidth = width;
28108             this.minHeight = height;
28109             
28110             if(this.rotate == 90 || this.rotate == 270){
28111                 this.minWidth = height;
28112                 this.minHeight = width;
28113             }
28114         }
28115         
28116         height = 300;
28117         width = Math.ceil(this.minWidth * height / this.minHeight);
28118         
28119         if(this.minWidth > this.minHeight){
28120             width = 300;
28121             height = Math.ceil(this.minHeight * width / this.minWidth);
28122         }
28123         
28124         this.thumbEl.setStyle({
28125             width : width + 'px',
28126             height : height + 'px'
28127         });
28128
28129         return;
28130             
28131     },
28132     
28133     setThumbBoxPosition : function()
28134     {
28135         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28136         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28137         
28138         this.thumbEl.setLeft(x);
28139         this.thumbEl.setTop(y);
28140         
28141     },
28142     
28143     baseRotateLevel : function()
28144     {
28145         this.baseRotate = 1;
28146         
28147         if(
28148                 typeof(this.exif) != 'undefined' &&
28149                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28150                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28151         ){
28152             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28153         }
28154         
28155         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28156         
28157     },
28158     
28159     baseScaleLevel : function()
28160     {
28161         var width, height;
28162         
28163         if(this.isDocument){
28164             
28165             if(this.baseRotate == 6 || this.baseRotate == 8){
28166             
28167                 height = this.thumbEl.getHeight();
28168                 this.baseScale = height / this.imageEl.OriginWidth;
28169
28170                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28171                     width = this.thumbEl.getWidth();
28172                     this.baseScale = width / this.imageEl.OriginHeight;
28173                 }
28174
28175                 return;
28176             }
28177
28178             height = this.thumbEl.getHeight();
28179             this.baseScale = height / this.imageEl.OriginHeight;
28180
28181             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28182                 width = this.thumbEl.getWidth();
28183                 this.baseScale = width / this.imageEl.OriginWidth;
28184             }
28185
28186             return;
28187         }
28188         
28189         if(this.baseRotate == 6 || this.baseRotate == 8){
28190             
28191             width = this.thumbEl.getHeight();
28192             this.baseScale = width / this.imageEl.OriginHeight;
28193             
28194             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28195                 height = this.thumbEl.getWidth();
28196                 this.baseScale = height / this.imageEl.OriginHeight;
28197             }
28198             
28199             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28200                 height = this.thumbEl.getWidth();
28201                 this.baseScale = height / this.imageEl.OriginHeight;
28202                 
28203                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28204                     width = this.thumbEl.getHeight();
28205                     this.baseScale = width / this.imageEl.OriginWidth;
28206                 }
28207             }
28208             
28209             return;
28210         }
28211         
28212         width = this.thumbEl.getWidth();
28213         this.baseScale = width / this.imageEl.OriginWidth;
28214         
28215         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28216             height = this.thumbEl.getHeight();
28217             this.baseScale = height / this.imageEl.OriginHeight;
28218         }
28219         
28220         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28221             
28222             height = this.thumbEl.getHeight();
28223             this.baseScale = height / this.imageEl.OriginHeight;
28224             
28225             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28226                 width = this.thumbEl.getWidth();
28227                 this.baseScale = width / this.imageEl.OriginWidth;
28228             }
28229             
28230         }
28231         
28232         return;
28233     },
28234     
28235     getScaleLevel : function()
28236     {
28237         return this.baseScale * Math.pow(1.1, this.scale);
28238     },
28239     
28240     onTouchStart : function(e)
28241     {
28242         if(!this.canvasLoaded){
28243             this.beforeSelectFile(e);
28244             return;
28245         }
28246         
28247         var touches = e.browserEvent.touches;
28248         
28249         if(!touches){
28250             return;
28251         }
28252         
28253         if(touches.length == 1){
28254             this.onMouseDown(e);
28255             return;
28256         }
28257         
28258         if(touches.length != 2){
28259             return;
28260         }
28261         
28262         var coords = [];
28263         
28264         for(var i = 0, finger; finger = touches[i]; i++){
28265             coords.push(finger.pageX, finger.pageY);
28266         }
28267         
28268         var x = Math.pow(coords[0] - coords[2], 2);
28269         var y = Math.pow(coords[1] - coords[3], 2);
28270         
28271         this.startDistance = Math.sqrt(x + y);
28272         
28273         this.startScale = this.scale;
28274         
28275         this.pinching = true;
28276         this.dragable = false;
28277         
28278     },
28279     
28280     onTouchMove : function(e)
28281     {
28282         if(!this.pinching && !this.dragable){
28283             return;
28284         }
28285         
28286         var touches = e.browserEvent.touches;
28287         
28288         if(!touches){
28289             return;
28290         }
28291         
28292         if(this.dragable){
28293             this.onMouseMove(e);
28294             return;
28295         }
28296         
28297         var coords = [];
28298         
28299         for(var i = 0, finger; finger = touches[i]; i++){
28300             coords.push(finger.pageX, finger.pageY);
28301         }
28302         
28303         var x = Math.pow(coords[0] - coords[2], 2);
28304         var y = Math.pow(coords[1] - coords[3], 2);
28305         
28306         this.endDistance = Math.sqrt(x + y);
28307         
28308         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28309         
28310         if(!this.zoomable()){
28311             this.scale = this.startScale;
28312             return;
28313         }
28314         
28315         this.draw();
28316         
28317     },
28318     
28319     onTouchEnd : function(e)
28320     {
28321         this.pinching = false;
28322         this.dragable = false;
28323         
28324     },
28325     
28326     process : function(file, crop)
28327     {
28328         if(this.loadMask){
28329             this.maskEl.mask(this.loadingText);
28330         }
28331         
28332         this.xhr = new XMLHttpRequest();
28333         
28334         file.xhr = this.xhr;
28335
28336         this.xhr.open(this.method, this.url, true);
28337         
28338         var headers = {
28339             "Accept": "application/json",
28340             "Cache-Control": "no-cache",
28341             "X-Requested-With": "XMLHttpRequest"
28342         };
28343         
28344         for (var headerName in headers) {
28345             var headerValue = headers[headerName];
28346             if (headerValue) {
28347                 this.xhr.setRequestHeader(headerName, headerValue);
28348             }
28349         }
28350         
28351         var _this = this;
28352         
28353         this.xhr.onload = function()
28354         {
28355             _this.xhrOnLoad(_this.xhr);
28356         }
28357         
28358         this.xhr.onerror = function()
28359         {
28360             _this.xhrOnError(_this.xhr);
28361         }
28362         
28363         var formData = new FormData();
28364
28365         formData.append('returnHTML', 'NO');
28366         
28367         if(crop){
28368             formData.append('crop', crop);
28369         }
28370         
28371         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28372             formData.append(this.paramName, file, file.name);
28373         }
28374         
28375         if(typeof(file.filename) != 'undefined'){
28376             formData.append('filename', file.filename);
28377         }
28378         
28379         if(typeof(file.mimetype) != 'undefined'){
28380             formData.append('mimetype', file.mimetype);
28381         }
28382         
28383         if(this.fireEvent('arrange', this, formData) != false){
28384             this.xhr.send(formData);
28385         };
28386     },
28387     
28388     xhrOnLoad : function(xhr)
28389     {
28390         if(this.loadMask){
28391             this.maskEl.unmask();
28392         }
28393         
28394         if (xhr.readyState !== 4) {
28395             this.fireEvent('exception', this, xhr);
28396             return;
28397         }
28398
28399         var response = Roo.decode(xhr.responseText);
28400         
28401         if(!response.success){
28402             this.fireEvent('exception', this, xhr);
28403             return;
28404         }
28405         
28406         var response = Roo.decode(xhr.responseText);
28407         
28408         this.fireEvent('upload', this, response);
28409         
28410     },
28411     
28412     xhrOnError : function()
28413     {
28414         if(this.loadMask){
28415             this.maskEl.unmask();
28416         }
28417         
28418         Roo.log('xhr on error');
28419         
28420         var response = Roo.decode(xhr.responseText);
28421           
28422         Roo.log(response);
28423         
28424     },
28425     
28426     prepare : function(file)
28427     {   
28428         if(this.loadMask){
28429             this.maskEl.mask(this.loadingText);
28430         }
28431         
28432         this.file = false;
28433         this.exif = {};
28434         
28435         if(typeof(file) === 'string'){
28436             this.loadCanvas(file);
28437             return;
28438         }
28439         
28440         if(!file || !this.urlAPI){
28441             return;
28442         }
28443         
28444         this.file = file;
28445         this.cropType = file.type;
28446         
28447         var _this = this;
28448         
28449         if(this.fireEvent('prepare', this, this.file) != false){
28450             
28451             var reader = new FileReader();
28452             
28453             reader.onload = function (e) {
28454                 if (e.target.error) {
28455                     Roo.log(e.target.error);
28456                     return;
28457                 }
28458                 
28459                 var buffer = e.target.result,
28460                     dataView = new DataView(buffer),
28461                     offset = 2,
28462                     maxOffset = dataView.byteLength - 4,
28463                     markerBytes,
28464                     markerLength;
28465                 
28466                 if (dataView.getUint16(0) === 0xffd8) {
28467                     while (offset < maxOffset) {
28468                         markerBytes = dataView.getUint16(offset);
28469                         
28470                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28471                             markerLength = dataView.getUint16(offset + 2) + 2;
28472                             if (offset + markerLength > dataView.byteLength) {
28473                                 Roo.log('Invalid meta data: Invalid segment size.');
28474                                 break;
28475                             }
28476                             
28477                             if(markerBytes == 0xffe1){
28478                                 _this.parseExifData(
28479                                     dataView,
28480                                     offset,
28481                                     markerLength
28482                                 );
28483                             }
28484                             
28485                             offset += markerLength;
28486                             
28487                             continue;
28488                         }
28489                         
28490                         break;
28491                     }
28492                     
28493                 }
28494                 
28495                 var url = _this.urlAPI.createObjectURL(_this.file);
28496                 
28497                 _this.loadCanvas(url);
28498                 
28499                 return;
28500             }
28501             
28502             reader.readAsArrayBuffer(this.file);
28503             
28504         }
28505         
28506     },
28507     
28508     parseExifData : function(dataView, offset, length)
28509     {
28510         var tiffOffset = offset + 10,
28511             littleEndian,
28512             dirOffset;
28513     
28514         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28515             // No Exif data, might be XMP data instead
28516             return;
28517         }
28518         
28519         // Check for the ASCII code for "Exif" (0x45786966):
28520         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28521             // No Exif data, might be XMP data instead
28522             return;
28523         }
28524         if (tiffOffset + 8 > dataView.byteLength) {
28525             Roo.log('Invalid Exif data: Invalid segment size.');
28526             return;
28527         }
28528         // Check for the two null bytes:
28529         if (dataView.getUint16(offset + 8) !== 0x0000) {
28530             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28531             return;
28532         }
28533         // Check the byte alignment:
28534         switch (dataView.getUint16(tiffOffset)) {
28535         case 0x4949:
28536             littleEndian = true;
28537             break;
28538         case 0x4D4D:
28539             littleEndian = false;
28540             break;
28541         default:
28542             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28543             return;
28544         }
28545         // Check for the TIFF tag marker (0x002A):
28546         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28547             Roo.log('Invalid Exif data: Missing TIFF marker.');
28548             return;
28549         }
28550         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28551         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28552         
28553         this.parseExifTags(
28554             dataView,
28555             tiffOffset,
28556             tiffOffset + dirOffset,
28557             littleEndian
28558         );
28559     },
28560     
28561     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28562     {
28563         var tagsNumber,
28564             dirEndOffset,
28565             i;
28566         if (dirOffset + 6 > dataView.byteLength) {
28567             Roo.log('Invalid Exif data: Invalid directory offset.');
28568             return;
28569         }
28570         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28571         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28572         if (dirEndOffset + 4 > dataView.byteLength) {
28573             Roo.log('Invalid Exif data: Invalid directory size.');
28574             return;
28575         }
28576         for (i = 0; i < tagsNumber; i += 1) {
28577             this.parseExifTag(
28578                 dataView,
28579                 tiffOffset,
28580                 dirOffset + 2 + 12 * i, // tag offset
28581                 littleEndian
28582             );
28583         }
28584         // Return the offset to the next directory:
28585         return dataView.getUint32(dirEndOffset, littleEndian);
28586     },
28587     
28588     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28589     {
28590         var tag = dataView.getUint16(offset, littleEndian);
28591         
28592         this.exif[tag] = this.getExifValue(
28593             dataView,
28594             tiffOffset,
28595             offset,
28596             dataView.getUint16(offset + 2, littleEndian), // tag type
28597             dataView.getUint32(offset + 4, littleEndian), // tag length
28598             littleEndian
28599         );
28600     },
28601     
28602     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28603     {
28604         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28605             tagSize,
28606             dataOffset,
28607             values,
28608             i,
28609             str,
28610             c;
28611     
28612         if (!tagType) {
28613             Roo.log('Invalid Exif data: Invalid tag type.');
28614             return;
28615         }
28616         
28617         tagSize = tagType.size * length;
28618         // Determine if the value is contained in the dataOffset bytes,
28619         // or if the value at the dataOffset is a pointer to the actual data:
28620         dataOffset = tagSize > 4 ?
28621                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28622         if (dataOffset + tagSize > dataView.byteLength) {
28623             Roo.log('Invalid Exif data: Invalid data offset.');
28624             return;
28625         }
28626         if (length === 1) {
28627             return tagType.getValue(dataView, dataOffset, littleEndian);
28628         }
28629         values = [];
28630         for (i = 0; i < length; i += 1) {
28631             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28632         }
28633         
28634         if (tagType.ascii) {
28635             str = '';
28636             // Concatenate the chars:
28637             for (i = 0; i < values.length; i += 1) {
28638                 c = values[i];
28639                 // Ignore the terminating NULL byte(s):
28640                 if (c === '\u0000') {
28641                     break;
28642                 }
28643                 str += c;
28644             }
28645             return str;
28646         }
28647         return values;
28648     }
28649     
28650 });
28651
28652 Roo.apply(Roo.bootstrap.UploadCropbox, {
28653     tags : {
28654         'Orientation': 0x0112
28655     },
28656     
28657     Orientation: {
28658             1: 0, //'top-left',
28659 //            2: 'top-right',
28660             3: 180, //'bottom-right',
28661 //            4: 'bottom-left',
28662 //            5: 'left-top',
28663             6: 90, //'right-top',
28664 //            7: 'right-bottom',
28665             8: 270 //'left-bottom'
28666     },
28667     
28668     exifTagTypes : {
28669         // byte, 8-bit unsigned int:
28670         1: {
28671             getValue: function (dataView, dataOffset) {
28672                 return dataView.getUint8(dataOffset);
28673             },
28674             size: 1
28675         },
28676         // ascii, 8-bit byte:
28677         2: {
28678             getValue: function (dataView, dataOffset) {
28679                 return String.fromCharCode(dataView.getUint8(dataOffset));
28680             },
28681             size: 1,
28682             ascii: true
28683         },
28684         // short, 16 bit int:
28685         3: {
28686             getValue: function (dataView, dataOffset, littleEndian) {
28687                 return dataView.getUint16(dataOffset, littleEndian);
28688             },
28689             size: 2
28690         },
28691         // long, 32 bit int:
28692         4: {
28693             getValue: function (dataView, dataOffset, littleEndian) {
28694                 return dataView.getUint32(dataOffset, littleEndian);
28695             },
28696             size: 4
28697         },
28698         // rational = two long values, first is numerator, second is denominator:
28699         5: {
28700             getValue: function (dataView, dataOffset, littleEndian) {
28701                 return dataView.getUint32(dataOffset, littleEndian) /
28702                     dataView.getUint32(dataOffset + 4, littleEndian);
28703             },
28704             size: 8
28705         },
28706         // slong, 32 bit signed int:
28707         9: {
28708             getValue: function (dataView, dataOffset, littleEndian) {
28709                 return dataView.getInt32(dataOffset, littleEndian);
28710             },
28711             size: 4
28712         },
28713         // srational, two slongs, first is numerator, second is denominator:
28714         10: {
28715             getValue: function (dataView, dataOffset, littleEndian) {
28716                 return dataView.getInt32(dataOffset, littleEndian) /
28717                     dataView.getInt32(dataOffset + 4, littleEndian);
28718             },
28719             size: 8
28720         }
28721     },
28722     
28723     footer : {
28724         STANDARD : [
28725             {
28726                 tag : 'div',
28727                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28728                 action : 'rotate-left',
28729                 cn : [
28730                     {
28731                         tag : 'button',
28732                         cls : 'btn btn-default',
28733                         html : '<i class="fa fa-undo"></i>'
28734                     }
28735                 ]
28736             },
28737             {
28738                 tag : 'div',
28739                 cls : 'btn-group roo-upload-cropbox-picture',
28740                 action : 'picture',
28741                 cn : [
28742                     {
28743                         tag : 'button',
28744                         cls : 'btn btn-default',
28745                         html : '<i class="fa fa-picture-o"></i>'
28746                     }
28747                 ]
28748             },
28749             {
28750                 tag : 'div',
28751                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28752                 action : 'rotate-right',
28753                 cn : [
28754                     {
28755                         tag : 'button',
28756                         cls : 'btn btn-default',
28757                         html : '<i class="fa fa-repeat"></i>'
28758                     }
28759                 ]
28760             }
28761         ],
28762         DOCUMENT : [
28763             {
28764                 tag : 'div',
28765                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28766                 action : 'rotate-left',
28767                 cn : [
28768                     {
28769                         tag : 'button',
28770                         cls : 'btn btn-default',
28771                         html : '<i class="fa fa-undo"></i>'
28772                     }
28773                 ]
28774             },
28775             {
28776                 tag : 'div',
28777                 cls : 'btn-group roo-upload-cropbox-download',
28778                 action : 'download',
28779                 cn : [
28780                     {
28781                         tag : 'button',
28782                         cls : 'btn btn-default',
28783                         html : '<i class="fa fa-download"></i>'
28784                     }
28785                 ]
28786             },
28787             {
28788                 tag : 'div',
28789                 cls : 'btn-group roo-upload-cropbox-crop',
28790                 action : 'crop',
28791                 cn : [
28792                     {
28793                         tag : 'button',
28794                         cls : 'btn btn-default',
28795                         html : '<i class="fa fa-crop"></i>'
28796                     }
28797                 ]
28798             },
28799             {
28800                 tag : 'div',
28801                 cls : 'btn-group roo-upload-cropbox-trash',
28802                 action : 'trash',
28803                 cn : [
28804                     {
28805                         tag : 'button',
28806                         cls : 'btn btn-default',
28807                         html : '<i class="fa fa-trash"></i>'
28808                     }
28809                 ]
28810             },
28811             {
28812                 tag : 'div',
28813                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28814                 action : 'rotate-right',
28815                 cn : [
28816                     {
28817                         tag : 'button',
28818                         cls : 'btn btn-default',
28819                         html : '<i class="fa fa-repeat"></i>'
28820                     }
28821                 ]
28822             }
28823         ],
28824         ROTATOR : [
28825             {
28826                 tag : 'div',
28827                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28828                 action : 'rotate-left',
28829                 cn : [
28830                     {
28831                         tag : 'button',
28832                         cls : 'btn btn-default',
28833                         html : '<i class="fa fa-undo"></i>'
28834                     }
28835                 ]
28836             },
28837             {
28838                 tag : 'div',
28839                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28840                 action : 'rotate-right',
28841                 cn : [
28842                     {
28843                         tag : 'button',
28844                         cls : 'btn btn-default',
28845                         html : '<i class="fa fa-repeat"></i>'
28846                     }
28847                 ]
28848             }
28849         ]
28850     }
28851 });
28852
28853 /*
28854 * Licence: LGPL
28855 */
28856
28857 /**
28858  * @class Roo.bootstrap.DocumentManager
28859  * @extends Roo.bootstrap.Component
28860  * Bootstrap DocumentManager class
28861  * @cfg {String} paramName default 'imageUpload'
28862  * @cfg {String} toolTipName default 'filename'
28863  * @cfg {String} method default POST
28864  * @cfg {String} url action url
28865  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28866  * @cfg {Boolean} multiple multiple upload default true
28867  * @cfg {Number} thumbSize default 300
28868  * @cfg {String} fieldLabel
28869  * @cfg {Number} labelWidth default 4
28870  * @cfg {String} labelAlign (left|top) default left
28871  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28872 * @cfg {Number} labellg set the width of label (1-12)
28873  * @cfg {Number} labelmd set the width of label (1-12)
28874  * @cfg {Number} labelsm set the width of label (1-12)
28875  * @cfg {Number} labelxs set the width of label (1-12)
28876  * 
28877  * @constructor
28878  * Create a new DocumentManager
28879  * @param {Object} config The config object
28880  */
28881
28882 Roo.bootstrap.DocumentManager = function(config){
28883     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28884     
28885     this.files = [];
28886     this.delegates = [];
28887     
28888     this.addEvents({
28889         /**
28890          * @event initial
28891          * Fire when initial the DocumentManager
28892          * @param {Roo.bootstrap.DocumentManager} this
28893          */
28894         "initial" : true,
28895         /**
28896          * @event inspect
28897          * inspect selected file
28898          * @param {Roo.bootstrap.DocumentManager} this
28899          * @param {File} file
28900          */
28901         "inspect" : true,
28902         /**
28903          * @event exception
28904          * Fire when xhr load exception
28905          * @param {Roo.bootstrap.DocumentManager} this
28906          * @param {XMLHttpRequest} xhr
28907          */
28908         "exception" : true,
28909         /**
28910          * @event afterupload
28911          * Fire when xhr load exception
28912          * @param {Roo.bootstrap.DocumentManager} this
28913          * @param {XMLHttpRequest} xhr
28914          */
28915         "afterupload" : true,
28916         /**
28917          * @event prepare
28918          * prepare the form data
28919          * @param {Roo.bootstrap.DocumentManager} this
28920          * @param {Object} formData
28921          */
28922         "prepare" : true,
28923         /**
28924          * @event remove
28925          * Fire when remove the file
28926          * @param {Roo.bootstrap.DocumentManager} this
28927          * @param {Object} file
28928          */
28929         "remove" : true,
28930         /**
28931          * @event refresh
28932          * Fire after refresh the file
28933          * @param {Roo.bootstrap.DocumentManager} this
28934          */
28935         "refresh" : true,
28936         /**
28937          * @event click
28938          * Fire after click the image
28939          * @param {Roo.bootstrap.DocumentManager} this
28940          * @param {Object} file
28941          */
28942         "click" : true,
28943         /**
28944          * @event edit
28945          * Fire when upload a image and editable set to true
28946          * @param {Roo.bootstrap.DocumentManager} this
28947          * @param {Object} file
28948          */
28949         "edit" : true,
28950         /**
28951          * @event beforeselectfile
28952          * Fire before select file
28953          * @param {Roo.bootstrap.DocumentManager} this
28954          */
28955         "beforeselectfile" : true,
28956         /**
28957          * @event process
28958          * Fire before process file
28959          * @param {Roo.bootstrap.DocumentManager} this
28960          * @param {Object} file
28961          */
28962         "process" : true,
28963         /**
28964          * @event previewrendered
28965          * Fire when preview rendered
28966          * @param {Roo.bootstrap.DocumentManager} this
28967          * @param {Object} file
28968          */
28969         "previewrendered" : true,
28970         /**
28971          */
28972         "previewResize" : true
28973         
28974     });
28975 };
28976
28977 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28978     
28979     boxes : 0,
28980     inputName : '',
28981     thumbSize : 300,
28982     multiple : true,
28983     files : false,
28984     method : 'POST',
28985     url : '',
28986     paramName : 'imageUpload',
28987     toolTipName : 'filename',
28988     fieldLabel : '',
28989     labelWidth : 4,
28990     labelAlign : 'left',
28991     editable : true,
28992     delegates : false,
28993     xhr : false, 
28994     
28995     labellg : 0,
28996     labelmd : 0,
28997     labelsm : 0,
28998     labelxs : 0,
28999     
29000     getAutoCreate : function()
29001     {   
29002         var managerWidget = {
29003             tag : 'div',
29004             cls : 'roo-document-manager',
29005             cn : [
29006                 {
29007                     tag : 'input',
29008                     cls : 'roo-document-manager-selector',
29009                     type : 'file'
29010                 },
29011                 {
29012                     tag : 'div',
29013                     cls : 'roo-document-manager-uploader',
29014                     cn : [
29015                         {
29016                             tag : 'div',
29017                             cls : 'roo-document-manager-upload-btn',
29018                             html : '<i class="fa fa-plus"></i>'
29019                         }
29020                     ]
29021                     
29022                 }
29023             ]
29024         };
29025         
29026         var content = [
29027             {
29028                 tag : 'div',
29029                 cls : 'column col-md-12',
29030                 cn : managerWidget
29031             }
29032         ];
29033         
29034         if(this.fieldLabel.length){
29035             
29036             content = [
29037                 {
29038                     tag : 'div',
29039                     cls : 'column col-md-12',
29040                     html : this.fieldLabel
29041                 },
29042                 {
29043                     tag : 'div',
29044                     cls : 'column col-md-12',
29045                     cn : managerWidget
29046                 }
29047             ];
29048
29049             if(this.labelAlign == 'left'){
29050                 content = [
29051                     {
29052                         tag : 'div',
29053                         cls : 'column',
29054                         html : this.fieldLabel
29055                     },
29056                     {
29057                         tag : 'div',
29058                         cls : 'column',
29059                         cn : managerWidget
29060                     }
29061                 ];
29062                 
29063                 if(this.labelWidth > 12){
29064                     content[0].style = "width: " + this.labelWidth + 'px';
29065                 }
29066
29067                 if(this.labelWidth < 13 && this.labelmd == 0){
29068                     this.labelmd = this.labelWidth;
29069                 }
29070
29071                 if(this.labellg > 0){
29072                     content[0].cls += ' col-lg-' + this.labellg;
29073                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29074                 }
29075
29076                 if(this.labelmd > 0){
29077                     content[0].cls += ' col-md-' + this.labelmd;
29078                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29079                 }
29080
29081                 if(this.labelsm > 0){
29082                     content[0].cls += ' col-sm-' + this.labelsm;
29083                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29084                 }
29085
29086                 if(this.labelxs > 0){
29087                     content[0].cls += ' col-xs-' + this.labelxs;
29088                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29089                 }
29090                 
29091             }
29092         }
29093         
29094         var cfg = {
29095             tag : 'div',
29096             cls : 'row clearfix',
29097             cn : content
29098         };
29099         
29100         return cfg;
29101         
29102     },
29103     
29104     initEvents : function()
29105     {
29106         this.managerEl = this.el.select('.roo-document-manager', true).first();
29107         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29108         
29109         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29110         this.selectorEl.hide();
29111         
29112         if(this.multiple){
29113             this.selectorEl.attr('multiple', 'multiple');
29114         }
29115         
29116         this.selectorEl.on('change', this.onFileSelected, this);
29117         
29118         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29119         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29120         
29121         this.uploader.on('click', this.onUploaderClick, this);
29122         
29123         this.renderProgressDialog();
29124         
29125         var _this = this;
29126         
29127         window.addEventListener("resize", function() { _this.refresh(); } );
29128         
29129         this.fireEvent('initial', this);
29130     },
29131     
29132     renderProgressDialog : function()
29133     {
29134         var _this = this;
29135         
29136         this.progressDialog = new Roo.bootstrap.Modal({
29137             cls : 'roo-document-manager-progress-dialog',
29138             allow_close : false,
29139             title : '',
29140             buttons : [
29141                 {
29142                     name  :'cancel',
29143                     weight : 'danger',
29144                     html : 'Cancel'
29145                 }
29146             ], 
29147             listeners : { 
29148                 btnclick : function() {
29149                     _this.uploadCancel();
29150                     this.hide();
29151                 }
29152             }
29153         });
29154          
29155         this.progressDialog.render(Roo.get(document.body));
29156          
29157         this.progress = new Roo.bootstrap.Progress({
29158             cls : 'roo-document-manager-progress',
29159             active : true,
29160             striped : true
29161         });
29162         
29163         this.progress.render(this.progressDialog.getChildContainer());
29164         
29165         this.progressBar = new Roo.bootstrap.ProgressBar({
29166             cls : 'roo-document-manager-progress-bar',
29167             aria_valuenow : 0,
29168             aria_valuemin : 0,
29169             aria_valuemax : 12,
29170             panel : 'success'
29171         });
29172         
29173         this.progressBar.render(this.progress.getChildContainer());
29174     },
29175     
29176     onUploaderClick : function(e)
29177     {
29178         e.preventDefault();
29179      
29180         if(this.fireEvent('beforeselectfile', this) != false){
29181             this.selectorEl.dom.click();
29182         }
29183         
29184     },
29185     
29186     onFileSelected : function(e)
29187     {
29188         e.preventDefault();
29189         
29190         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29191             return;
29192         }
29193         
29194         Roo.each(this.selectorEl.dom.files, function(file){
29195             if(this.fireEvent('inspect', this, file) != false){
29196                 this.files.push(file);
29197             }
29198         }, this);
29199         
29200         this.queue();
29201         
29202     },
29203     
29204     queue : function()
29205     {
29206         this.selectorEl.dom.value = '';
29207         
29208         if(!this.files || !this.files.length){
29209             return;
29210         }
29211         
29212         if(this.boxes > 0 && this.files.length > this.boxes){
29213             this.files = this.files.slice(0, this.boxes);
29214         }
29215         
29216         this.uploader.show();
29217         
29218         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29219             this.uploader.hide();
29220         }
29221         
29222         var _this = this;
29223         
29224         var files = [];
29225         
29226         var docs = [];
29227         
29228         Roo.each(this.files, function(file){
29229             
29230             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29231                 var f = this.renderPreview(file);
29232                 files.push(f);
29233                 return;
29234             }
29235             
29236             if(file.type.indexOf('image') != -1){
29237                 this.delegates.push(
29238                     (function(){
29239                         _this.process(file);
29240                     }).createDelegate(this)
29241                 );
29242         
29243                 return;
29244             }
29245             
29246             docs.push(
29247                 (function(){
29248                     _this.process(file);
29249                 }).createDelegate(this)
29250             );
29251             
29252         }, this);
29253         
29254         this.files = files;
29255         
29256         this.delegates = this.delegates.concat(docs);
29257         
29258         if(!this.delegates.length){
29259             this.refresh();
29260             return;
29261         }
29262         
29263         this.progressBar.aria_valuemax = this.delegates.length;
29264         
29265         this.arrange();
29266         
29267         return;
29268     },
29269     
29270     arrange : function()
29271     {
29272         if(!this.delegates.length){
29273             this.progressDialog.hide();
29274             this.refresh();
29275             return;
29276         }
29277         
29278         var delegate = this.delegates.shift();
29279         
29280         this.progressDialog.show();
29281         
29282         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29283         
29284         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29285         
29286         delegate();
29287     },
29288     
29289     refresh : function()
29290     {
29291         this.uploader.show();
29292         
29293         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29294             this.uploader.hide();
29295         }
29296         
29297         Roo.isTouch ? this.closable(false) : this.closable(true);
29298         
29299         this.fireEvent('refresh', this);
29300     },
29301     
29302     onRemove : function(e, el, o)
29303     {
29304         e.preventDefault();
29305         
29306         this.fireEvent('remove', this, o);
29307         
29308     },
29309     
29310     remove : function(o)
29311     {
29312         var files = [];
29313         
29314         Roo.each(this.files, function(file){
29315             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29316                 files.push(file);
29317                 return;
29318             }
29319
29320             o.target.remove();
29321
29322         }, this);
29323         
29324         this.files = files;
29325         
29326         this.refresh();
29327     },
29328     
29329     clear : function()
29330     {
29331         Roo.each(this.files, function(file){
29332             if(!file.target){
29333                 return;
29334             }
29335             
29336             file.target.remove();
29337
29338         }, this);
29339         
29340         this.files = [];
29341         
29342         this.refresh();
29343     },
29344     
29345     onClick : function(e, el, o)
29346     {
29347         e.preventDefault();
29348         
29349         this.fireEvent('click', this, o);
29350         
29351     },
29352     
29353     closable : function(closable)
29354     {
29355         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29356             
29357             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29358             
29359             if(closable){
29360                 el.show();
29361                 return;
29362             }
29363             
29364             el.hide();
29365             
29366         }, this);
29367     },
29368     
29369     xhrOnLoad : function(xhr)
29370     {
29371         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29372             el.remove();
29373         }, this);
29374         
29375         if (xhr.readyState !== 4) {
29376             this.arrange();
29377             this.fireEvent('exception', this, xhr);
29378             return;
29379         }
29380
29381         var response = Roo.decode(xhr.responseText);
29382         
29383         if(!response.success){
29384             this.arrange();
29385             this.fireEvent('exception', this, xhr);
29386             return;
29387         }
29388         
29389         var file = this.renderPreview(response.data);
29390         
29391         this.files.push(file);
29392         
29393         this.arrange();
29394         
29395         this.fireEvent('afterupload', this, xhr);
29396         
29397     },
29398     
29399     xhrOnError : function(xhr)
29400     {
29401         Roo.log('xhr on error');
29402         
29403         var response = Roo.decode(xhr.responseText);
29404           
29405         Roo.log(response);
29406         
29407         this.arrange();
29408     },
29409     
29410     process : function(file)
29411     {
29412         if(this.fireEvent('process', this, file) !== false){
29413             if(this.editable && file.type.indexOf('image') != -1){
29414                 this.fireEvent('edit', this, file);
29415                 return;
29416             }
29417
29418             this.uploadStart(file, false);
29419
29420             return;
29421         }
29422         
29423     },
29424     
29425     uploadStart : function(file, crop)
29426     {
29427         this.xhr = new XMLHttpRequest();
29428         
29429         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29430             this.arrange();
29431             return;
29432         }
29433         
29434         file.xhr = this.xhr;
29435             
29436         this.managerEl.createChild({
29437             tag : 'div',
29438             cls : 'roo-document-manager-loading',
29439             cn : [
29440                 {
29441                     tag : 'div',
29442                     tooltip : file.name,
29443                     cls : 'roo-document-manager-thumb',
29444                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29445                 }
29446             ]
29447
29448         });
29449
29450         this.xhr.open(this.method, this.url, true);
29451         
29452         var headers = {
29453             "Accept": "application/json",
29454             "Cache-Control": "no-cache",
29455             "X-Requested-With": "XMLHttpRequest"
29456         };
29457         
29458         for (var headerName in headers) {
29459             var headerValue = headers[headerName];
29460             if (headerValue) {
29461                 this.xhr.setRequestHeader(headerName, headerValue);
29462             }
29463         }
29464         
29465         var _this = this;
29466         
29467         this.xhr.onload = function()
29468         {
29469             _this.xhrOnLoad(_this.xhr);
29470         }
29471         
29472         this.xhr.onerror = function()
29473         {
29474             _this.xhrOnError(_this.xhr);
29475         }
29476         
29477         var formData = new FormData();
29478
29479         formData.append('returnHTML', 'NO');
29480         
29481         if(crop){
29482             formData.append('crop', crop);
29483         }
29484         
29485         formData.append(this.paramName, file, file.name);
29486         
29487         var options = {
29488             file : file, 
29489             manually : false
29490         };
29491         
29492         if(this.fireEvent('prepare', this, formData, options) != false){
29493             
29494             if(options.manually){
29495                 return;
29496             }
29497             
29498             this.xhr.send(formData);
29499             return;
29500         };
29501         
29502         this.uploadCancel();
29503     },
29504     
29505     uploadCancel : function()
29506     {
29507         if (this.xhr) {
29508             this.xhr.abort();
29509         }
29510         
29511         this.delegates = [];
29512         
29513         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29514             el.remove();
29515         }, this);
29516         
29517         this.arrange();
29518     },
29519     
29520     renderPreview : function(file)
29521     {
29522         if(typeof(file.target) != 'undefined' && file.target){
29523             return file;
29524         }
29525         
29526         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29527         
29528         var previewEl = this.managerEl.createChild({
29529             tag : 'div',
29530             cls : 'roo-document-manager-preview',
29531             cn : [
29532                 {
29533                     tag : 'div',
29534                     tooltip : file[this.toolTipName],
29535                     cls : 'roo-document-manager-thumb',
29536                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29537                 },
29538                 {
29539                     tag : 'button',
29540                     cls : 'close',
29541                     html : '<i class="fa fa-times-circle"></i>'
29542                 }
29543             ]
29544         });
29545
29546         var close = previewEl.select('button.close', true).first();
29547
29548         close.on('click', this.onRemove, this, file);
29549
29550         file.target = previewEl;
29551
29552         var image = previewEl.select('img', true).first();
29553         
29554         var _this = this;
29555         
29556         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29557         
29558         image.on('click', this.onClick, this, file);
29559         
29560         this.fireEvent('previewrendered', this, file);
29561         
29562         return file;
29563         
29564     },
29565     
29566     onPreviewLoad : function(file, image)
29567     {
29568         if(typeof(file.target) == 'undefined' || !file.target){
29569             return;
29570         }
29571         
29572         var width = image.dom.naturalWidth || image.dom.width;
29573         var height = image.dom.naturalHeight || image.dom.height;
29574         
29575         if(!this.previewResize) {
29576             return;
29577         }
29578         
29579         if(width > height){
29580             file.target.addClass('wide');
29581             return;
29582         }
29583         
29584         file.target.addClass('tall');
29585         return;
29586         
29587     },
29588     
29589     uploadFromSource : function(file, crop)
29590     {
29591         this.xhr = new XMLHttpRequest();
29592         
29593         this.managerEl.createChild({
29594             tag : 'div',
29595             cls : 'roo-document-manager-loading',
29596             cn : [
29597                 {
29598                     tag : 'div',
29599                     tooltip : file.name,
29600                     cls : 'roo-document-manager-thumb',
29601                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29602                 }
29603             ]
29604
29605         });
29606
29607         this.xhr.open(this.method, this.url, true);
29608         
29609         var headers = {
29610             "Accept": "application/json",
29611             "Cache-Control": "no-cache",
29612             "X-Requested-With": "XMLHttpRequest"
29613         };
29614         
29615         for (var headerName in headers) {
29616             var headerValue = headers[headerName];
29617             if (headerValue) {
29618                 this.xhr.setRequestHeader(headerName, headerValue);
29619             }
29620         }
29621         
29622         var _this = this;
29623         
29624         this.xhr.onload = function()
29625         {
29626             _this.xhrOnLoad(_this.xhr);
29627         }
29628         
29629         this.xhr.onerror = function()
29630         {
29631             _this.xhrOnError(_this.xhr);
29632         }
29633         
29634         var formData = new FormData();
29635
29636         formData.append('returnHTML', 'NO');
29637         
29638         formData.append('crop', crop);
29639         
29640         if(typeof(file.filename) != 'undefined'){
29641             formData.append('filename', file.filename);
29642         }
29643         
29644         if(typeof(file.mimetype) != 'undefined'){
29645             formData.append('mimetype', file.mimetype);
29646         }
29647         
29648         Roo.log(formData);
29649         
29650         if(this.fireEvent('prepare', this, formData) != false){
29651             this.xhr.send(formData);
29652         };
29653     }
29654 });
29655
29656 /*
29657 * Licence: LGPL
29658 */
29659
29660 /**
29661  * @class Roo.bootstrap.DocumentViewer
29662  * @extends Roo.bootstrap.Component
29663  * Bootstrap DocumentViewer class
29664  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29665  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29666  * 
29667  * @constructor
29668  * Create a new DocumentViewer
29669  * @param {Object} config The config object
29670  */
29671
29672 Roo.bootstrap.DocumentViewer = function(config){
29673     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29674     
29675     this.addEvents({
29676         /**
29677          * @event initial
29678          * Fire after initEvent
29679          * @param {Roo.bootstrap.DocumentViewer} this
29680          */
29681         "initial" : true,
29682         /**
29683          * @event click
29684          * Fire after click
29685          * @param {Roo.bootstrap.DocumentViewer} this
29686          */
29687         "click" : true,
29688         /**
29689          * @event download
29690          * Fire after download button
29691          * @param {Roo.bootstrap.DocumentViewer} this
29692          */
29693         "download" : true,
29694         /**
29695          * @event trash
29696          * Fire after trash button
29697          * @param {Roo.bootstrap.DocumentViewer} this
29698          */
29699         "trash" : true
29700         
29701     });
29702 };
29703
29704 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29705     
29706     showDownload : true,
29707     
29708     showTrash : true,
29709     
29710     getAutoCreate : function()
29711     {
29712         var cfg = {
29713             tag : 'div',
29714             cls : 'roo-document-viewer',
29715             cn : [
29716                 {
29717                     tag : 'div',
29718                     cls : 'roo-document-viewer-body',
29719                     cn : [
29720                         {
29721                             tag : 'div',
29722                             cls : 'roo-document-viewer-thumb',
29723                             cn : [
29724                                 {
29725                                     tag : 'img',
29726                                     cls : 'roo-document-viewer-image'
29727                                 }
29728                             ]
29729                         }
29730                     ]
29731                 },
29732                 {
29733                     tag : 'div',
29734                     cls : 'roo-document-viewer-footer',
29735                     cn : {
29736                         tag : 'div',
29737                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29738                         cn : [
29739                             {
29740                                 tag : 'div',
29741                                 cls : 'btn-group roo-document-viewer-download',
29742                                 cn : [
29743                                     {
29744                                         tag : 'button',
29745                                         cls : 'btn btn-default',
29746                                         html : '<i class="fa fa-download"></i>'
29747                                     }
29748                                 ]
29749                             },
29750                             {
29751                                 tag : 'div',
29752                                 cls : 'btn-group roo-document-viewer-trash',
29753                                 cn : [
29754                                     {
29755                                         tag : 'button',
29756                                         cls : 'btn btn-default',
29757                                         html : '<i class="fa fa-trash"></i>'
29758                                     }
29759                                 ]
29760                             }
29761                         ]
29762                     }
29763                 }
29764             ]
29765         };
29766         
29767         return cfg;
29768     },
29769     
29770     initEvents : function()
29771     {
29772         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29773         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29774         
29775         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29776         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29777         
29778         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29779         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29780         
29781         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29782         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29783         
29784         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29785         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29786         
29787         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29788         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29789         
29790         this.bodyEl.on('click', this.onClick, this);
29791         this.downloadBtn.on('click', this.onDownload, this);
29792         this.trashBtn.on('click', this.onTrash, this);
29793         
29794         this.downloadBtn.hide();
29795         this.trashBtn.hide();
29796         
29797         if(this.showDownload){
29798             this.downloadBtn.show();
29799         }
29800         
29801         if(this.showTrash){
29802             this.trashBtn.show();
29803         }
29804         
29805         if(!this.showDownload && !this.showTrash) {
29806             this.footerEl.hide();
29807         }
29808         
29809     },
29810     
29811     initial : function()
29812     {
29813         this.fireEvent('initial', this);
29814         
29815     },
29816     
29817     onClick : function(e)
29818     {
29819         e.preventDefault();
29820         
29821         this.fireEvent('click', this);
29822     },
29823     
29824     onDownload : function(e)
29825     {
29826         e.preventDefault();
29827         
29828         this.fireEvent('download', this);
29829     },
29830     
29831     onTrash : function(e)
29832     {
29833         e.preventDefault();
29834         
29835         this.fireEvent('trash', this);
29836     }
29837     
29838 });
29839 /*
29840  * - LGPL
29841  *
29842  * nav progress bar
29843  * 
29844  */
29845
29846 /**
29847  * @class Roo.bootstrap.NavProgressBar
29848  * @extends Roo.bootstrap.Component
29849  * Bootstrap NavProgressBar class
29850  * 
29851  * @constructor
29852  * Create a new nav progress bar
29853  * @param {Object} config The config object
29854  */
29855
29856 Roo.bootstrap.NavProgressBar = function(config){
29857     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29858
29859     this.bullets = this.bullets || [];
29860    
29861 //    Roo.bootstrap.NavProgressBar.register(this);
29862      this.addEvents({
29863         /**
29864              * @event changed
29865              * Fires when the active item changes
29866              * @param {Roo.bootstrap.NavProgressBar} this
29867              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29868              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29869          */
29870         'changed': true
29871      });
29872     
29873 };
29874
29875 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29876     
29877     bullets : [],
29878     barItems : [],
29879     
29880     getAutoCreate : function()
29881     {
29882         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29883         
29884         cfg = {
29885             tag : 'div',
29886             cls : 'roo-navigation-bar-group',
29887             cn : [
29888                 {
29889                     tag : 'div',
29890                     cls : 'roo-navigation-top-bar'
29891                 },
29892                 {
29893                     tag : 'div',
29894                     cls : 'roo-navigation-bullets-bar',
29895                     cn : [
29896                         {
29897                             tag : 'ul',
29898                             cls : 'roo-navigation-bar'
29899                         }
29900                     ]
29901                 },
29902                 
29903                 {
29904                     tag : 'div',
29905                     cls : 'roo-navigation-bottom-bar'
29906                 }
29907             ]
29908             
29909         };
29910         
29911         return cfg;
29912         
29913     },
29914     
29915     initEvents: function() 
29916     {
29917         
29918     },
29919     
29920     onRender : function(ct, position) 
29921     {
29922         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29923         
29924         if(this.bullets.length){
29925             Roo.each(this.bullets, function(b){
29926                this.addItem(b);
29927             }, this);
29928         }
29929         
29930         this.format();
29931         
29932     },
29933     
29934     addItem : function(cfg)
29935     {
29936         var item = new Roo.bootstrap.NavProgressItem(cfg);
29937         
29938         item.parentId = this.id;
29939         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29940         
29941         if(cfg.html){
29942             var top = new Roo.bootstrap.Element({
29943                 tag : 'div',
29944                 cls : 'roo-navigation-bar-text'
29945             });
29946             
29947             var bottom = new Roo.bootstrap.Element({
29948                 tag : 'div',
29949                 cls : 'roo-navigation-bar-text'
29950             });
29951             
29952             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29953             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29954             
29955             var topText = new Roo.bootstrap.Element({
29956                 tag : 'span',
29957                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29958             });
29959             
29960             var bottomText = new Roo.bootstrap.Element({
29961                 tag : 'span',
29962                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29963             });
29964             
29965             topText.onRender(top.el, null);
29966             bottomText.onRender(bottom.el, null);
29967             
29968             item.topEl = top;
29969             item.bottomEl = bottom;
29970         }
29971         
29972         this.barItems.push(item);
29973         
29974         return item;
29975     },
29976     
29977     getActive : function()
29978     {
29979         var active = false;
29980         
29981         Roo.each(this.barItems, function(v){
29982             
29983             if (!v.isActive()) {
29984                 return;
29985             }
29986             
29987             active = v;
29988             return false;
29989             
29990         });
29991         
29992         return active;
29993     },
29994     
29995     setActiveItem : function(item)
29996     {
29997         var prev = false;
29998         
29999         Roo.each(this.barItems, function(v){
30000             if (v.rid == item.rid) {
30001                 return ;
30002             }
30003             
30004             if (v.isActive()) {
30005                 v.setActive(false);
30006                 prev = v;
30007             }
30008         });
30009
30010         item.setActive(true);
30011         
30012         this.fireEvent('changed', this, item, prev);
30013     },
30014     
30015     getBarItem: function(rid)
30016     {
30017         var ret = false;
30018         
30019         Roo.each(this.barItems, function(e) {
30020             if (e.rid != rid) {
30021                 return;
30022             }
30023             
30024             ret =  e;
30025             return false;
30026         });
30027         
30028         return ret;
30029     },
30030     
30031     indexOfItem : function(item)
30032     {
30033         var index = false;
30034         
30035         Roo.each(this.barItems, function(v, i){
30036             
30037             if (v.rid != item.rid) {
30038                 return;
30039             }
30040             
30041             index = i;
30042             return false
30043         });
30044         
30045         return index;
30046     },
30047     
30048     setActiveNext : function()
30049     {
30050         var i = this.indexOfItem(this.getActive());
30051         
30052         if (i > this.barItems.length) {
30053             return;
30054         }
30055         
30056         this.setActiveItem(this.barItems[i+1]);
30057     },
30058     
30059     setActivePrev : function()
30060     {
30061         var i = this.indexOfItem(this.getActive());
30062         
30063         if (i  < 1) {
30064             return;
30065         }
30066         
30067         this.setActiveItem(this.barItems[i-1]);
30068     },
30069     
30070     format : function()
30071     {
30072         if(!this.barItems.length){
30073             return;
30074         }
30075      
30076         var width = 100 / this.barItems.length;
30077         
30078         Roo.each(this.barItems, function(i){
30079             i.el.setStyle('width', width + '%');
30080             i.topEl.el.setStyle('width', width + '%');
30081             i.bottomEl.el.setStyle('width', width + '%');
30082         }, this);
30083         
30084     }
30085     
30086 });
30087 /*
30088  * - LGPL
30089  *
30090  * Nav Progress Item
30091  * 
30092  */
30093
30094 /**
30095  * @class Roo.bootstrap.NavProgressItem
30096  * @extends Roo.bootstrap.Component
30097  * Bootstrap NavProgressItem class
30098  * @cfg {String} rid the reference id
30099  * @cfg {Boolean} active (true|false) Is item active default false
30100  * @cfg {Boolean} disabled (true|false) Is item active default false
30101  * @cfg {String} html
30102  * @cfg {String} position (top|bottom) text position default bottom
30103  * @cfg {String} icon show icon instead of number
30104  * 
30105  * @constructor
30106  * Create a new NavProgressItem
30107  * @param {Object} config The config object
30108  */
30109 Roo.bootstrap.NavProgressItem = function(config){
30110     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30111     this.addEvents({
30112         // raw events
30113         /**
30114          * @event click
30115          * The raw click event for the entire grid.
30116          * @param {Roo.bootstrap.NavProgressItem} this
30117          * @param {Roo.EventObject} e
30118          */
30119         "click" : true
30120     });
30121    
30122 };
30123
30124 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30125     
30126     rid : '',
30127     active : false,
30128     disabled : false,
30129     html : '',
30130     position : 'bottom',
30131     icon : false,
30132     
30133     getAutoCreate : function()
30134     {
30135         var iconCls = 'roo-navigation-bar-item-icon';
30136         
30137         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30138         
30139         var cfg = {
30140             tag: 'li',
30141             cls: 'roo-navigation-bar-item',
30142             cn : [
30143                 {
30144                     tag : 'i',
30145                     cls : iconCls
30146                 }
30147             ]
30148         };
30149         
30150         if(this.active){
30151             cfg.cls += ' active';
30152         }
30153         if(this.disabled){
30154             cfg.cls += ' disabled';
30155         }
30156         
30157         return cfg;
30158     },
30159     
30160     disable : function()
30161     {
30162         this.setDisabled(true);
30163     },
30164     
30165     enable : function()
30166     {
30167         this.setDisabled(false);
30168     },
30169     
30170     initEvents: function() 
30171     {
30172         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30173         
30174         this.iconEl.on('click', this.onClick, this);
30175     },
30176     
30177     onClick : function(e)
30178     {
30179         e.preventDefault();
30180         
30181         if(this.disabled){
30182             return;
30183         }
30184         
30185         if(this.fireEvent('click', this, e) === false){
30186             return;
30187         };
30188         
30189         this.parent().setActiveItem(this);
30190     },
30191     
30192     isActive: function () 
30193     {
30194         return this.active;
30195     },
30196     
30197     setActive : function(state)
30198     {
30199         if(this.active == state){
30200             return;
30201         }
30202         
30203         this.active = state;
30204         
30205         if (state) {
30206             this.el.addClass('active');
30207             return;
30208         }
30209         
30210         this.el.removeClass('active');
30211         
30212         return;
30213     },
30214     
30215     setDisabled : function(state)
30216     {
30217         if(this.disabled == state){
30218             return;
30219         }
30220         
30221         this.disabled = state;
30222         
30223         if (state) {
30224             this.el.addClass('disabled');
30225             return;
30226         }
30227         
30228         this.el.removeClass('disabled');
30229     },
30230     
30231     tooltipEl : function()
30232     {
30233         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30234     }
30235 });
30236  
30237
30238  /*
30239  * - LGPL
30240  *
30241  * FieldLabel
30242  * 
30243  */
30244
30245 /**
30246  * @class Roo.bootstrap.FieldLabel
30247  * @extends Roo.bootstrap.Component
30248  * Bootstrap FieldLabel class
30249  * @cfg {String} html contents of the element
30250  * @cfg {String} tag tag of the element default label
30251  * @cfg {String} cls class of the element
30252  * @cfg {String} target label target 
30253  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30254  * @cfg {String} invalidClass default "text-warning"
30255  * @cfg {String} validClass default "text-success"
30256  * @cfg {String} iconTooltip default "This field is required"
30257  * @cfg {String} indicatorpos (left|right) default left
30258  * 
30259  * @constructor
30260  * Create a new FieldLabel
30261  * @param {Object} config The config object
30262  */
30263
30264 Roo.bootstrap.FieldLabel = function(config){
30265     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30266     
30267     this.addEvents({
30268             /**
30269              * @event invalid
30270              * Fires after the field has been marked as invalid.
30271              * @param {Roo.form.FieldLabel} this
30272              * @param {String} msg The validation message
30273              */
30274             invalid : true,
30275             /**
30276              * @event valid
30277              * Fires after the field has been validated with no errors.
30278              * @param {Roo.form.FieldLabel} this
30279              */
30280             valid : true
30281         });
30282 };
30283
30284 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30285     
30286     tag: 'label',
30287     cls: '',
30288     html: '',
30289     target: '',
30290     allowBlank : true,
30291     invalidClass : 'has-warning',
30292     validClass : 'has-success',
30293     iconTooltip : 'This field is required',
30294     indicatorpos : 'left',
30295     
30296     getAutoCreate : function(){
30297         
30298         var cls = "";
30299         if (!this.allowBlank) {
30300             cls  = "visible";
30301         }
30302         
30303         var cfg = {
30304             tag : this.tag,
30305             cls : 'roo-bootstrap-field-label ' + this.cls,
30306             for : this.target,
30307             cn : [
30308                 {
30309                     tag : 'i',
30310                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30311                     tooltip : this.iconTooltip
30312                 },
30313                 {
30314                     tag : 'span',
30315                     html : this.html
30316                 }
30317             ] 
30318         };
30319         
30320         if(this.indicatorpos == 'right'){
30321             var cfg = {
30322                 tag : this.tag,
30323                 cls : 'roo-bootstrap-field-label ' + this.cls,
30324                 for : this.target,
30325                 cn : [
30326                     {
30327                         tag : 'span',
30328                         html : this.html
30329                     },
30330                     {
30331                         tag : 'i',
30332                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30333                         tooltip : this.iconTooltip
30334                     }
30335                 ] 
30336             };
30337         }
30338         
30339         return cfg;
30340     },
30341     
30342     initEvents: function() 
30343     {
30344         Roo.bootstrap.Element.superclass.initEvents.call(this);
30345         
30346         this.indicator = this.indicatorEl();
30347         
30348         if(this.indicator){
30349             this.indicator.removeClass('visible');
30350             this.indicator.addClass('invisible');
30351         }
30352         
30353         Roo.bootstrap.FieldLabel.register(this);
30354     },
30355     
30356     indicatorEl : function()
30357     {
30358         var indicator = this.el.select('i.roo-required-indicator',true).first();
30359         
30360         if(!indicator){
30361             return false;
30362         }
30363         
30364         return indicator;
30365         
30366     },
30367     
30368     /**
30369      * Mark this field as valid
30370      */
30371     markValid : function()
30372     {
30373         if(this.indicator){
30374             this.indicator.removeClass('visible');
30375             this.indicator.addClass('invisible');
30376         }
30377         
30378         this.el.removeClass(this.invalidClass);
30379         
30380         this.el.addClass(this.validClass);
30381         
30382         this.fireEvent('valid', this);
30383     },
30384     
30385     /**
30386      * Mark this field as invalid
30387      * @param {String} msg The validation message
30388      */
30389     markInvalid : function(msg)
30390     {
30391         if(this.indicator){
30392             this.indicator.removeClass('invisible');
30393             this.indicator.addClass('visible');
30394         }
30395         
30396         this.el.removeClass(this.validClass);
30397         
30398         this.el.addClass(this.invalidClass);
30399         
30400         this.fireEvent('invalid', this, msg);
30401     }
30402     
30403    
30404 });
30405
30406 Roo.apply(Roo.bootstrap.FieldLabel, {
30407     
30408     groups: {},
30409     
30410      /**
30411     * register a FieldLabel Group
30412     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30413     */
30414     register : function(label)
30415     {
30416         if(this.groups.hasOwnProperty(label.target)){
30417             return;
30418         }
30419      
30420         this.groups[label.target] = label;
30421         
30422     },
30423     /**
30424     * fetch a FieldLabel Group based on the target
30425     * @param {string} target
30426     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30427     */
30428     get: function(target) {
30429         if (typeof(this.groups[target]) == 'undefined') {
30430             return false;
30431         }
30432         
30433         return this.groups[target] ;
30434     }
30435 });
30436
30437  
30438
30439  /*
30440  * - LGPL
30441  *
30442  * page DateSplitField.
30443  * 
30444  */
30445
30446
30447 /**
30448  * @class Roo.bootstrap.DateSplitField
30449  * @extends Roo.bootstrap.Component
30450  * Bootstrap DateSplitField class
30451  * @cfg {string} fieldLabel - the label associated
30452  * @cfg {Number} labelWidth set the width of label (0-12)
30453  * @cfg {String} labelAlign (top|left)
30454  * @cfg {Boolean} dayAllowBlank (true|false) default false
30455  * @cfg {Boolean} monthAllowBlank (true|false) default false
30456  * @cfg {Boolean} yearAllowBlank (true|false) default false
30457  * @cfg {string} dayPlaceholder 
30458  * @cfg {string} monthPlaceholder
30459  * @cfg {string} yearPlaceholder
30460  * @cfg {string} dayFormat default 'd'
30461  * @cfg {string} monthFormat default 'm'
30462  * @cfg {string} yearFormat default 'Y'
30463  * @cfg {Number} labellg set the width of label (1-12)
30464  * @cfg {Number} labelmd set the width of label (1-12)
30465  * @cfg {Number} labelsm set the width of label (1-12)
30466  * @cfg {Number} labelxs set the width of label (1-12)
30467
30468  *     
30469  * @constructor
30470  * Create a new DateSplitField
30471  * @param {Object} config The config object
30472  */
30473
30474 Roo.bootstrap.DateSplitField = function(config){
30475     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30476     
30477     this.addEvents({
30478         // raw events
30479          /**
30480          * @event years
30481          * getting the data of years
30482          * @param {Roo.bootstrap.DateSplitField} this
30483          * @param {Object} years
30484          */
30485         "years" : true,
30486         /**
30487          * @event days
30488          * getting the data of days
30489          * @param {Roo.bootstrap.DateSplitField} this
30490          * @param {Object} days
30491          */
30492         "days" : true,
30493         /**
30494          * @event invalid
30495          * Fires after the field has been marked as invalid.
30496          * @param {Roo.form.Field} this
30497          * @param {String} msg The validation message
30498          */
30499         invalid : true,
30500        /**
30501          * @event valid
30502          * Fires after the field has been validated with no errors.
30503          * @param {Roo.form.Field} this
30504          */
30505         valid : true
30506     });
30507 };
30508
30509 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30510     
30511     fieldLabel : '',
30512     labelAlign : 'top',
30513     labelWidth : 3,
30514     dayAllowBlank : false,
30515     monthAllowBlank : false,
30516     yearAllowBlank : false,
30517     dayPlaceholder : '',
30518     monthPlaceholder : '',
30519     yearPlaceholder : '',
30520     dayFormat : 'd',
30521     monthFormat : 'm',
30522     yearFormat : 'Y',
30523     isFormField : true,
30524     labellg : 0,
30525     labelmd : 0,
30526     labelsm : 0,
30527     labelxs : 0,
30528     
30529     getAutoCreate : function()
30530     {
30531         var cfg = {
30532             tag : 'div',
30533             cls : 'row roo-date-split-field-group',
30534             cn : [
30535                 {
30536                     tag : 'input',
30537                     type : 'hidden',
30538                     cls : 'form-hidden-field roo-date-split-field-group-value',
30539                     name : this.name
30540                 }
30541             ]
30542         };
30543         
30544         var labelCls = 'col-md-12';
30545         var contentCls = 'col-md-4';
30546         
30547         if(this.fieldLabel){
30548             
30549             var label = {
30550                 tag : 'div',
30551                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30552                 cn : [
30553                     {
30554                         tag : 'label',
30555                         html : this.fieldLabel
30556                     }
30557                 ]
30558             };
30559             
30560             if(this.labelAlign == 'left'){
30561             
30562                 if(this.labelWidth > 12){
30563                     label.style = "width: " + this.labelWidth + 'px';
30564                 }
30565
30566                 if(this.labelWidth < 13 && this.labelmd == 0){
30567                     this.labelmd = this.labelWidth;
30568                 }
30569
30570                 if(this.labellg > 0){
30571                     labelCls = ' col-lg-' + this.labellg;
30572                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30573                 }
30574
30575                 if(this.labelmd > 0){
30576                     labelCls = ' col-md-' + this.labelmd;
30577                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30578                 }
30579
30580                 if(this.labelsm > 0){
30581                     labelCls = ' col-sm-' + this.labelsm;
30582                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30583                 }
30584
30585                 if(this.labelxs > 0){
30586                     labelCls = ' col-xs-' + this.labelxs;
30587                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30588                 }
30589             }
30590             
30591             label.cls += ' ' + labelCls;
30592             
30593             cfg.cn.push(label);
30594         }
30595         
30596         Roo.each(['day', 'month', 'year'], function(t){
30597             cfg.cn.push({
30598                 tag : 'div',
30599                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30600             });
30601         }, this);
30602         
30603         return cfg;
30604     },
30605     
30606     inputEl: function ()
30607     {
30608         return this.el.select('.roo-date-split-field-group-value', true).first();
30609     },
30610     
30611     onRender : function(ct, position) 
30612     {
30613         var _this = this;
30614         
30615         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30616         
30617         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30618         
30619         this.dayField = new Roo.bootstrap.ComboBox({
30620             allowBlank : this.dayAllowBlank,
30621             alwaysQuery : true,
30622             displayField : 'value',
30623             editable : false,
30624             fieldLabel : '',
30625             forceSelection : true,
30626             mode : 'local',
30627             placeholder : this.dayPlaceholder,
30628             selectOnFocus : true,
30629             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30630             triggerAction : 'all',
30631             typeAhead : true,
30632             valueField : 'value',
30633             store : new Roo.data.SimpleStore({
30634                 data : (function() {    
30635                     var days = [];
30636                     _this.fireEvent('days', _this, days);
30637                     return days;
30638                 })(),
30639                 fields : [ 'value' ]
30640             }),
30641             listeners : {
30642                 select : function (_self, record, index)
30643                 {
30644                     _this.setValue(_this.getValue());
30645                 }
30646             }
30647         });
30648
30649         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30650         
30651         this.monthField = new Roo.bootstrap.MonthField({
30652             after : '<i class=\"fa fa-calendar\"></i>',
30653             allowBlank : this.monthAllowBlank,
30654             placeholder : this.monthPlaceholder,
30655             readOnly : true,
30656             listeners : {
30657                 render : function (_self)
30658                 {
30659                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30660                         e.preventDefault();
30661                         _self.focus();
30662                     });
30663                 },
30664                 select : function (_self, oldvalue, newvalue)
30665                 {
30666                     _this.setValue(_this.getValue());
30667                 }
30668             }
30669         });
30670         
30671         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30672         
30673         this.yearField = new Roo.bootstrap.ComboBox({
30674             allowBlank : this.yearAllowBlank,
30675             alwaysQuery : true,
30676             displayField : 'value',
30677             editable : false,
30678             fieldLabel : '',
30679             forceSelection : true,
30680             mode : 'local',
30681             placeholder : this.yearPlaceholder,
30682             selectOnFocus : true,
30683             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30684             triggerAction : 'all',
30685             typeAhead : true,
30686             valueField : 'value',
30687             store : new Roo.data.SimpleStore({
30688                 data : (function() {
30689                     var years = [];
30690                     _this.fireEvent('years', _this, years);
30691                     return years;
30692                 })(),
30693                 fields : [ 'value' ]
30694             }),
30695             listeners : {
30696                 select : function (_self, record, index)
30697                 {
30698                     _this.setValue(_this.getValue());
30699                 }
30700             }
30701         });
30702
30703         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30704     },
30705     
30706     setValue : function(v, format)
30707     {
30708         this.inputEl.dom.value = v;
30709         
30710         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30711         
30712         var d = Date.parseDate(v, f);
30713         
30714         if(!d){
30715             this.validate();
30716             return;
30717         }
30718         
30719         this.setDay(d.format(this.dayFormat));
30720         this.setMonth(d.format(this.monthFormat));
30721         this.setYear(d.format(this.yearFormat));
30722         
30723         this.validate();
30724         
30725         return;
30726     },
30727     
30728     setDay : function(v)
30729     {
30730         this.dayField.setValue(v);
30731         this.inputEl.dom.value = this.getValue();
30732         this.validate();
30733         return;
30734     },
30735     
30736     setMonth : function(v)
30737     {
30738         this.monthField.setValue(v, true);
30739         this.inputEl.dom.value = this.getValue();
30740         this.validate();
30741         return;
30742     },
30743     
30744     setYear : function(v)
30745     {
30746         this.yearField.setValue(v);
30747         this.inputEl.dom.value = this.getValue();
30748         this.validate();
30749         return;
30750     },
30751     
30752     getDay : function()
30753     {
30754         return this.dayField.getValue();
30755     },
30756     
30757     getMonth : function()
30758     {
30759         return this.monthField.getValue();
30760     },
30761     
30762     getYear : function()
30763     {
30764         return this.yearField.getValue();
30765     },
30766     
30767     getValue : function()
30768     {
30769         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30770         
30771         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30772         
30773         return date;
30774     },
30775     
30776     reset : function()
30777     {
30778         this.setDay('');
30779         this.setMonth('');
30780         this.setYear('');
30781         this.inputEl.dom.value = '';
30782         this.validate();
30783         return;
30784     },
30785     
30786     validate : function()
30787     {
30788         var d = this.dayField.validate();
30789         var m = this.monthField.validate();
30790         var y = this.yearField.validate();
30791         
30792         var valid = true;
30793         
30794         if(
30795                 (!this.dayAllowBlank && !d) ||
30796                 (!this.monthAllowBlank && !m) ||
30797                 (!this.yearAllowBlank && !y)
30798         ){
30799             valid = false;
30800         }
30801         
30802         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30803             return valid;
30804         }
30805         
30806         if(valid){
30807             this.markValid();
30808             return valid;
30809         }
30810         
30811         this.markInvalid();
30812         
30813         return valid;
30814     },
30815     
30816     markValid : function()
30817     {
30818         
30819         var label = this.el.select('label', true).first();
30820         var icon = this.el.select('i.fa-star', true).first();
30821
30822         if(label && icon){
30823             icon.remove();
30824         }
30825         
30826         this.fireEvent('valid', this);
30827     },
30828     
30829      /**
30830      * Mark this field as invalid
30831      * @param {String} msg The validation message
30832      */
30833     markInvalid : function(msg)
30834     {
30835         
30836         var label = this.el.select('label', true).first();
30837         var icon = this.el.select('i.fa-star', true).first();
30838
30839         if(label && !icon){
30840             this.el.select('.roo-date-split-field-label', true).createChild({
30841                 tag : 'i',
30842                 cls : 'text-danger fa fa-lg fa-star',
30843                 tooltip : 'This field is required',
30844                 style : 'margin-right:5px;'
30845             }, label, true);
30846         }
30847         
30848         this.fireEvent('invalid', this, msg);
30849     },
30850     
30851     clearInvalid : function()
30852     {
30853         var label = this.el.select('label', true).first();
30854         var icon = this.el.select('i.fa-star', true).first();
30855
30856         if(label && icon){
30857             icon.remove();
30858         }
30859         
30860         this.fireEvent('valid', this);
30861     },
30862     
30863     getName: function()
30864     {
30865         return this.name;
30866     }
30867     
30868 });
30869
30870  /**
30871  *
30872  * This is based on 
30873  * http://masonry.desandro.com
30874  *
30875  * The idea is to render all the bricks based on vertical width...
30876  *
30877  * The original code extends 'outlayer' - we might need to use that....
30878  * 
30879  */
30880
30881
30882 /**
30883  * @class Roo.bootstrap.LayoutMasonry
30884  * @extends Roo.bootstrap.Component
30885  * Bootstrap Layout Masonry class
30886  * 
30887  * @constructor
30888  * Create a new Element
30889  * @param {Object} config The config object
30890  */
30891
30892 Roo.bootstrap.LayoutMasonry = function(config){
30893     
30894     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30895     
30896     this.bricks = [];
30897     
30898     Roo.bootstrap.LayoutMasonry.register(this);
30899     
30900     this.addEvents({
30901         // raw events
30902         /**
30903          * @event layout
30904          * Fire after layout the items
30905          * @param {Roo.bootstrap.LayoutMasonry} this
30906          * @param {Roo.EventObject} e
30907          */
30908         "layout" : true
30909     });
30910     
30911 };
30912
30913 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30914     
30915     /**
30916      * @cfg {Boolean} isLayoutInstant = no animation?
30917      */   
30918     isLayoutInstant : false, // needed?
30919    
30920     /**
30921      * @cfg {Number} boxWidth  width of the columns
30922      */   
30923     boxWidth : 450,
30924     
30925       /**
30926      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30927      */   
30928     boxHeight : 0,
30929     
30930     /**
30931      * @cfg {Number} padWidth padding below box..
30932      */   
30933     padWidth : 10, 
30934     
30935     /**
30936      * @cfg {Number} gutter gutter width..
30937      */   
30938     gutter : 10,
30939     
30940      /**
30941      * @cfg {Number} maxCols maximum number of columns
30942      */   
30943     
30944     maxCols: 0,
30945     
30946     /**
30947      * @cfg {Boolean} isAutoInitial defalut true
30948      */   
30949     isAutoInitial : true, 
30950     
30951     containerWidth: 0,
30952     
30953     /**
30954      * @cfg {Boolean} isHorizontal defalut false
30955      */   
30956     isHorizontal : false, 
30957
30958     currentSize : null,
30959     
30960     tag: 'div',
30961     
30962     cls: '',
30963     
30964     bricks: null, //CompositeElement
30965     
30966     cols : 1,
30967     
30968     _isLayoutInited : false,
30969     
30970 //    isAlternative : false, // only use for vertical layout...
30971     
30972     /**
30973      * @cfg {Number} alternativePadWidth padding below box..
30974      */   
30975     alternativePadWidth : 50,
30976     
30977     selectedBrick : [],
30978     
30979     getAutoCreate : function(){
30980         
30981         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30982         
30983         var cfg = {
30984             tag: this.tag,
30985             cls: 'blog-masonary-wrapper ' + this.cls,
30986             cn : {
30987                 cls : 'mas-boxes masonary'
30988             }
30989         };
30990         
30991         return cfg;
30992     },
30993     
30994     getChildContainer: function( )
30995     {
30996         if (this.boxesEl) {
30997             return this.boxesEl;
30998         }
30999         
31000         this.boxesEl = this.el.select('.mas-boxes').first();
31001         
31002         return this.boxesEl;
31003     },
31004     
31005     
31006     initEvents : function()
31007     {
31008         var _this = this;
31009         
31010         if(this.isAutoInitial){
31011             Roo.log('hook children rendered');
31012             this.on('childrenrendered', function() {
31013                 Roo.log('children rendered');
31014                 _this.initial();
31015             } ,this);
31016         }
31017     },
31018     
31019     initial : function()
31020     {
31021         this.selectedBrick = [];
31022         
31023         this.currentSize = this.el.getBox(true);
31024         
31025         Roo.EventManager.onWindowResize(this.resize, this); 
31026
31027         if(!this.isAutoInitial){
31028             this.layout();
31029             return;
31030         }
31031         
31032         this.layout();
31033         
31034         return;
31035         //this.layout.defer(500,this);
31036         
31037     },
31038     
31039     resize : function()
31040     {
31041         var cs = this.el.getBox(true);
31042         
31043         if (
31044                 this.currentSize.width == cs.width && 
31045                 this.currentSize.x == cs.x && 
31046                 this.currentSize.height == cs.height && 
31047                 this.currentSize.y == cs.y 
31048         ) {
31049             Roo.log("no change in with or X or Y");
31050             return;
31051         }
31052         
31053         this.currentSize = cs;
31054         
31055         this.layout();
31056         
31057     },
31058     
31059     layout : function()
31060     {   
31061         this._resetLayout();
31062         
31063         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31064         
31065         this.layoutItems( isInstant );
31066       
31067         this._isLayoutInited = true;
31068         
31069         this.fireEvent('layout', this);
31070         
31071     },
31072     
31073     _resetLayout : function()
31074     {
31075         if(this.isHorizontal){
31076             this.horizontalMeasureColumns();
31077             return;
31078         }
31079         
31080         this.verticalMeasureColumns();
31081         
31082     },
31083     
31084     verticalMeasureColumns : function()
31085     {
31086         this.getContainerWidth();
31087         
31088 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31089 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31090 //            return;
31091 //        }
31092         
31093         var boxWidth = this.boxWidth + this.padWidth;
31094         
31095         if(this.containerWidth < this.boxWidth){
31096             boxWidth = this.containerWidth
31097         }
31098         
31099         var containerWidth = this.containerWidth;
31100         
31101         var cols = Math.floor(containerWidth / boxWidth);
31102         
31103         this.cols = Math.max( cols, 1 );
31104         
31105         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31106         
31107         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31108         
31109         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31110         
31111         this.colWidth = boxWidth + avail - this.padWidth;
31112         
31113         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31114         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31115     },
31116     
31117     horizontalMeasureColumns : function()
31118     {
31119         this.getContainerWidth();
31120         
31121         var boxWidth = this.boxWidth;
31122         
31123         if(this.containerWidth < boxWidth){
31124             boxWidth = this.containerWidth;
31125         }
31126         
31127         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31128         
31129         this.el.setHeight(boxWidth);
31130         
31131     },
31132     
31133     getContainerWidth : function()
31134     {
31135         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31136     },
31137     
31138     layoutItems : function( isInstant )
31139     {
31140         Roo.log(this.bricks);
31141         
31142         var items = Roo.apply([], this.bricks);
31143         
31144         if(this.isHorizontal){
31145             this._horizontalLayoutItems( items , isInstant );
31146             return;
31147         }
31148         
31149 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31150 //            this._verticalAlternativeLayoutItems( items , isInstant );
31151 //            return;
31152 //        }
31153         
31154         this._verticalLayoutItems( items , isInstant );
31155         
31156     },
31157     
31158     _verticalLayoutItems : function ( items , isInstant)
31159     {
31160         if ( !items || !items.length ) {
31161             return;
31162         }
31163         
31164         var standard = [
31165             ['xs', 'xs', 'xs', 'tall'],
31166             ['xs', 'xs', 'tall'],
31167             ['xs', 'xs', 'sm'],
31168             ['xs', 'xs', 'xs'],
31169             ['xs', 'tall'],
31170             ['xs', 'sm'],
31171             ['xs', 'xs'],
31172             ['xs'],
31173             
31174             ['sm', 'xs', 'xs'],
31175             ['sm', 'xs'],
31176             ['sm'],
31177             
31178             ['tall', 'xs', 'xs', 'xs'],
31179             ['tall', 'xs', 'xs'],
31180             ['tall', 'xs'],
31181             ['tall']
31182             
31183         ];
31184         
31185         var queue = [];
31186         
31187         var boxes = [];
31188         
31189         var box = [];
31190         
31191         Roo.each(items, function(item, k){
31192             
31193             switch (item.size) {
31194                 // these layouts take up a full box,
31195                 case 'md' :
31196                 case 'md-left' :
31197                 case 'md-right' :
31198                 case 'wide' :
31199                     
31200                     if(box.length){
31201                         boxes.push(box);
31202                         box = [];
31203                     }
31204                     
31205                     boxes.push([item]);
31206                     
31207                     break;
31208                     
31209                 case 'xs' :
31210                 case 'sm' :
31211                 case 'tall' :
31212                     
31213                     box.push(item);
31214                     
31215                     break;
31216                 default :
31217                     break;
31218                     
31219             }
31220             
31221         }, this);
31222         
31223         if(box.length){
31224             boxes.push(box);
31225             box = [];
31226         }
31227         
31228         var filterPattern = function(box, length)
31229         {
31230             if(!box.length){
31231                 return;
31232             }
31233             
31234             var match = false;
31235             
31236             var pattern = box.slice(0, length);
31237             
31238             var format = [];
31239             
31240             Roo.each(pattern, function(i){
31241                 format.push(i.size);
31242             }, this);
31243             
31244             Roo.each(standard, function(s){
31245                 
31246                 if(String(s) != String(format)){
31247                     return;
31248                 }
31249                 
31250                 match = true;
31251                 return false;
31252                 
31253             }, this);
31254             
31255             if(!match && length == 1){
31256                 return;
31257             }
31258             
31259             if(!match){
31260                 filterPattern(box, length - 1);
31261                 return;
31262             }
31263                 
31264             queue.push(pattern);
31265
31266             box = box.slice(length, box.length);
31267
31268             filterPattern(box, 4);
31269
31270             return;
31271             
31272         }
31273         
31274         Roo.each(boxes, function(box, k){
31275             
31276             if(!box.length){
31277                 return;
31278             }
31279             
31280             if(box.length == 1){
31281                 queue.push(box);
31282                 return;
31283             }
31284             
31285             filterPattern(box, 4);
31286             
31287         }, this);
31288         
31289         this._processVerticalLayoutQueue( queue, isInstant );
31290         
31291     },
31292     
31293 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31294 //    {
31295 //        if ( !items || !items.length ) {
31296 //            return;
31297 //        }
31298 //
31299 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31300 //        
31301 //    },
31302     
31303     _horizontalLayoutItems : function ( items , isInstant)
31304     {
31305         if ( !items || !items.length || items.length < 3) {
31306             return;
31307         }
31308         
31309         items.reverse();
31310         
31311         var eItems = items.slice(0, 3);
31312         
31313         items = items.slice(3, items.length);
31314         
31315         var standard = [
31316             ['xs', 'xs', 'xs', 'wide'],
31317             ['xs', 'xs', 'wide'],
31318             ['xs', 'xs', 'sm'],
31319             ['xs', 'xs', 'xs'],
31320             ['xs', 'wide'],
31321             ['xs', 'sm'],
31322             ['xs', 'xs'],
31323             ['xs'],
31324             
31325             ['sm', 'xs', 'xs'],
31326             ['sm', 'xs'],
31327             ['sm'],
31328             
31329             ['wide', 'xs', 'xs', 'xs'],
31330             ['wide', 'xs', 'xs'],
31331             ['wide', 'xs'],
31332             ['wide'],
31333             
31334             ['wide-thin']
31335         ];
31336         
31337         var queue = [];
31338         
31339         var boxes = [];
31340         
31341         var box = [];
31342         
31343         Roo.each(items, function(item, k){
31344             
31345             switch (item.size) {
31346                 case 'md' :
31347                 case 'md-left' :
31348                 case 'md-right' :
31349                 case 'tall' :
31350                     
31351                     if(box.length){
31352                         boxes.push(box);
31353                         box = [];
31354                     }
31355                     
31356                     boxes.push([item]);
31357                     
31358                     break;
31359                     
31360                 case 'xs' :
31361                 case 'sm' :
31362                 case 'wide' :
31363                 case 'wide-thin' :
31364                     
31365                     box.push(item);
31366                     
31367                     break;
31368                 default :
31369                     break;
31370                     
31371             }
31372             
31373         }, this);
31374         
31375         if(box.length){
31376             boxes.push(box);
31377             box = [];
31378         }
31379         
31380         var filterPattern = function(box, length)
31381         {
31382             if(!box.length){
31383                 return;
31384             }
31385             
31386             var match = false;
31387             
31388             var pattern = box.slice(0, length);
31389             
31390             var format = [];
31391             
31392             Roo.each(pattern, function(i){
31393                 format.push(i.size);
31394             }, this);
31395             
31396             Roo.each(standard, function(s){
31397                 
31398                 if(String(s) != String(format)){
31399                     return;
31400                 }
31401                 
31402                 match = true;
31403                 return false;
31404                 
31405             }, this);
31406             
31407             if(!match && length == 1){
31408                 return;
31409             }
31410             
31411             if(!match){
31412                 filterPattern(box, length - 1);
31413                 return;
31414             }
31415                 
31416             queue.push(pattern);
31417
31418             box = box.slice(length, box.length);
31419
31420             filterPattern(box, 4);
31421
31422             return;
31423             
31424         }
31425         
31426         Roo.each(boxes, function(box, k){
31427             
31428             if(!box.length){
31429                 return;
31430             }
31431             
31432             if(box.length == 1){
31433                 queue.push(box);
31434                 return;
31435             }
31436             
31437             filterPattern(box, 4);
31438             
31439         }, this);
31440         
31441         
31442         var prune = [];
31443         
31444         var pos = this.el.getBox(true);
31445         
31446         var minX = pos.x;
31447         
31448         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31449         
31450         var hit_end = false;
31451         
31452         Roo.each(queue, function(box){
31453             
31454             if(hit_end){
31455                 
31456                 Roo.each(box, function(b){
31457                 
31458                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31459                     b.el.hide();
31460
31461                 }, this);
31462
31463                 return;
31464             }
31465             
31466             var mx = 0;
31467             
31468             Roo.each(box, function(b){
31469                 
31470                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31471                 b.el.show();
31472
31473                 mx = Math.max(mx, b.x);
31474                 
31475             }, this);
31476             
31477             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31478             
31479             if(maxX < minX){
31480                 
31481                 Roo.each(box, function(b){
31482                 
31483                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31484                     b.el.hide();
31485                     
31486                 }, this);
31487                 
31488                 hit_end = true;
31489                 
31490                 return;
31491             }
31492             
31493             prune.push(box);
31494             
31495         }, this);
31496         
31497         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31498     },
31499     
31500     /** Sets position of item in DOM
31501     * @param {Element} item
31502     * @param {Number} x - horizontal position
31503     * @param {Number} y - vertical position
31504     * @param {Boolean} isInstant - disables transitions
31505     */
31506     _processVerticalLayoutQueue : function( queue, isInstant )
31507     {
31508         var pos = this.el.getBox(true);
31509         var x = pos.x;
31510         var y = pos.y;
31511         var maxY = [];
31512         
31513         for (var i = 0; i < this.cols; i++){
31514             maxY[i] = pos.y;
31515         }
31516         
31517         Roo.each(queue, function(box, k){
31518             
31519             var col = k % this.cols;
31520             
31521             Roo.each(box, function(b,kk){
31522                 
31523                 b.el.position('absolute');
31524                 
31525                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31526                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31527                 
31528                 if(b.size == 'md-left' || b.size == 'md-right'){
31529                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31530                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31531                 }
31532                 
31533                 b.el.setWidth(width);
31534                 b.el.setHeight(height);
31535                 // iframe?
31536                 b.el.select('iframe',true).setSize(width,height);
31537                 
31538             }, this);
31539             
31540             for (var i = 0; i < this.cols; i++){
31541                 
31542                 if(maxY[i] < maxY[col]){
31543                     col = i;
31544                     continue;
31545                 }
31546                 
31547                 col = Math.min(col, i);
31548                 
31549             }
31550             
31551             x = pos.x + col * (this.colWidth + this.padWidth);
31552             
31553             y = maxY[col];
31554             
31555             var positions = [];
31556             
31557             switch (box.length){
31558                 case 1 :
31559                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31560                     break;
31561                 case 2 :
31562                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31563                     break;
31564                 case 3 :
31565                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31566                     break;
31567                 case 4 :
31568                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31569                     break;
31570                 default :
31571                     break;
31572             }
31573             
31574             Roo.each(box, function(b,kk){
31575                 
31576                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31577                 
31578                 var sz = b.el.getSize();
31579                 
31580                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31581                 
31582             }, this);
31583             
31584         }, this);
31585         
31586         var mY = 0;
31587         
31588         for (var i = 0; i < this.cols; i++){
31589             mY = Math.max(mY, maxY[i]);
31590         }
31591         
31592         this.el.setHeight(mY - pos.y);
31593         
31594     },
31595     
31596 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31597 //    {
31598 //        var pos = this.el.getBox(true);
31599 //        var x = pos.x;
31600 //        var y = pos.y;
31601 //        var maxX = pos.right;
31602 //        
31603 //        var maxHeight = 0;
31604 //        
31605 //        Roo.each(items, function(item, k){
31606 //            
31607 //            var c = k % 2;
31608 //            
31609 //            item.el.position('absolute');
31610 //                
31611 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31612 //
31613 //            item.el.setWidth(width);
31614 //
31615 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31616 //
31617 //            item.el.setHeight(height);
31618 //            
31619 //            if(c == 0){
31620 //                item.el.setXY([x, y], isInstant ? false : true);
31621 //            } else {
31622 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31623 //            }
31624 //            
31625 //            y = y + height + this.alternativePadWidth;
31626 //            
31627 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31628 //            
31629 //        }, this);
31630 //        
31631 //        this.el.setHeight(maxHeight);
31632 //        
31633 //    },
31634     
31635     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31636     {
31637         var pos = this.el.getBox(true);
31638         
31639         var minX = pos.x;
31640         var minY = pos.y;
31641         
31642         var maxX = pos.right;
31643         
31644         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31645         
31646         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31647         
31648         Roo.each(queue, function(box, k){
31649             
31650             Roo.each(box, function(b, kk){
31651                 
31652                 b.el.position('absolute');
31653                 
31654                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31655                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31656                 
31657                 if(b.size == 'md-left' || b.size == 'md-right'){
31658                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31659                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31660                 }
31661                 
31662                 b.el.setWidth(width);
31663                 b.el.setHeight(height);
31664                 
31665             }, this);
31666             
31667             if(!box.length){
31668                 return;
31669             }
31670             
31671             var positions = [];
31672             
31673             switch (box.length){
31674                 case 1 :
31675                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31676                     break;
31677                 case 2 :
31678                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31679                     break;
31680                 case 3 :
31681                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31682                     break;
31683                 case 4 :
31684                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31685                     break;
31686                 default :
31687                     break;
31688             }
31689             
31690             Roo.each(box, function(b,kk){
31691                 
31692                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31693                 
31694                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31695                 
31696             }, this);
31697             
31698         }, this);
31699         
31700     },
31701     
31702     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31703     {
31704         Roo.each(eItems, function(b,k){
31705             
31706             b.size = (k == 0) ? 'sm' : 'xs';
31707             b.x = (k == 0) ? 2 : 1;
31708             b.y = (k == 0) ? 2 : 1;
31709             
31710             b.el.position('absolute');
31711             
31712             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31713                 
31714             b.el.setWidth(width);
31715             
31716             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31717             
31718             b.el.setHeight(height);
31719             
31720         }, this);
31721
31722         var positions = [];
31723         
31724         positions.push({
31725             x : maxX - this.unitWidth * 2 - this.gutter,
31726             y : minY
31727         });
31728         
31729         positions.push({
31730             x : maxX - this.unitWidth,
31731             y : minY + (this.unitWidth + this.gutter) * 2
31732         });
31733         
31734         positions.push({
31735             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31736             y : minY
31737         });
31738         
31739         Roo.each(eItems, function(b,k){
31740             
31741             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31742
31743         }, this);
31744         
31745     },
31746     
31747     getVerticalOneBoxColPositions : function(x, y, box)
31748     {
31749         var pos = [];
31750         
31751         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31752         
31753         if(box[0].size == 'md-left'){
31754             rand = 0;
31755         }
31756         
31757         if(box[0].size == 'md-right'){
31758             rand = 1;
31759         }
31760         
31761         pos.push({
31762             x : x + (this.unitWidth + this.gutter) * rand,
31763             y : y
31764         });
31765         
31766         return pos;
31767     },
31768     
31769     getVerticalTwoBoxColPositions : function(x, y, box)
31770     {
31771         var pos = [];
31772         
31773         if(box[0].size == 'xs'){
31774             
31775             pos.push({
31776                 x : x,
31777                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31778             });
31779
31780             pos.push({
31781                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31782                 y : y
31783             });
31784             
31785             return pos;
31786             
31787         }
31788         
31789         pos.push({
31790             x : x,
31791             y : y
31792         });
31793
31794         pos.push({
31795             x : x + (this.unitWidth + this.gutter) * 2,
31796             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31797         });
31798         
31799         return pos;
31800         
31801     },
31802     
31803     getVerticalThreeBoxColPositions : function(x, y, box)
31804     {
31805         var pos = [];
31806         
31807         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31808             
31809             pos.push({
31810                 x : x,
31811                 y : y
31812             });
31813
31814             pos.push({
31815                 x : x + (this.unitWidth + this.gutter) * 1,
31816                 y : y
31817             });
31818             
31819             pos.push({
31820                 x : x + (this.unitWidth + this.gutter) * 2,
31821                 y : y
31822             });
31823             
31824             return pos;
31825             
31826         }
31827         
31828         if(box[0].size == 'xs' && box[1].size == 'xs'){
31829             
31830             pos.push({
31831                 x : x,
31832                 y : y
31833             });
31834
31835             pos.push({
31836                 x : x,
31837                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31838             });
31839             
31840             pos.push({
31841                 x : x + (this.unitWidth + this.gutter) * 1,
31842                 y : y
31843             });
31844             
31845             return pos;
31846             
31847         }
31848         
31849         pos.push({
31850             x : x,
31851             y : y
31852         });
31853
31854         pos.push({
31855             x : x + (this.unitWidth + this.gutter) * 2,
31856             y : y
31857         });
31858
31859         pos.push({
31860             x : x + (this.unitWidth + this.gutter) * 2,
31861             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31862         });
31863             
31864         return pos;
31865         
31866     },
31867     
31868     getVerticalFourBoxColPositions : function(x, y, box)
31869     {
31870         var pos = [];
31871         
31872         if(box[0].size == 'xs'){
31873             
31874             pos.push({
31875                 x : x,
31876                 y : y
31877             });
31878
31879             pos.push({
31880                 x : x,
31881                 y : y + (this.unitHeight + this.gutter) * 1
31882             });
31883             
31884             pos.push({
31885                 x : x,
31886                 y : y + (this.unitHeight + this.gutter) * 2
31887             });
31888             
31889             pos.push({
31890                 x : x + (this.unitWidth + this.gutter) * 1,
31891                 y : y
31892             });
31893             
31894             return pos;
31895             
31896         }
31897         
31898         pos.push({
31899             x : x,
31900             y : y
31901         });
31902
31903         pos.push({
31904             x : x + (this.unitWidth + this.gutter) * 2,
31905             y : y
31906         });
31907
31908         pos.push({
31909             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31910             y : y + (this.unitHeight + this.gutter) * 1
31911         });
31912
31913         pos.push({
31914             x : x + (this.unitWidth + this.gutter) * 2,
31915             y : y + (this.unitWidth + this.gutter) * 2
31916         });
31917
31918         return pos;
31919         
31920     },
31921     
31922     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31923     {
31924         var pos = [];
31925         
31926         if(box[0].size == 'md-left'){
31927             pos.push({
31928                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31929                 y : minY
31930             });
31931             
31932             return pos;
31933         }
31934         
31935         if(box[0].size == 'md-right'){
31936             pos.push({
31937                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31938                 y : minY + (this.unitWidth + this.gutter) * 1
31939             });
31940             
31941             return pos;
31942         }
31943         
31944         var rand = Math.floor(Math.random() * (4 - box[0].y));
31945         
31946         pos.push({
31947             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31948             y : minY + (this.unitWidth + this.gutter) * rand
31949         });
31950         
31951         return pos;
31952         
31953     },
31954     
31955     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31956     {
31957         var pos = [];
31958         
31959         if(box[0].size == 'xs'){
31960             
31961             pos.push({
31962                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31963                 y : minY
31964             });
31965
31966             pos.push({
31967                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31968                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31969             });
31970             
31971             return pos;
31972             
31973         }
31974         
31975         pos.push({
31976             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31977             y : minY
31978         });
31979
31980         pos.push({
31981             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31982             y : minY + (this.unitWidth + this.gutter) * 2
31983         });
31984         
31985         return pos;
31986         
31987     },
31988     
31989     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31990     {
31991         var pos = [];
31992         
31993         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31994             
31995             pos.push({
31996                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31997                 y : minY
31998             });
31999
32000             pos.push({
32001                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32002                 y : minY + (this.unitWidth + this.gutter) * 1
32003             });
32004             
32005             pos.push({
32006                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32007                 y : minY + (this.unitWidth + this.gutter) * 2
32008             });
32009             
32010             return pos;
32011             
32012         }
32013         
32014         if(box[0].size == 'xs' && box[1].size == 'xs'){
32015             
32016             pos.push({
32017                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32018                 y : minY
32019             });
32020
32021             pos.push({
32022                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32023                 y : minY
32024             });
32025             
32026             pos.push({
32027                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32028                 y : minY + (this.unitWidth + this.gutter) * 1
32029             });
32030             
32031             return pos;
32032             
32033         }
32034         
32035         pos.push({
32036             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32037             y : minY
32038         });
32039
32040         pos.push({
32041             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32042             y : minY + (this.unitWidth + this.gutter) * 2
32043         });
32044
32045         pos.push({
32046             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32047             y : minY + (this.unitWidth + this.gutter) * 2
32048         });
32049             
32050         return pos;
32051         
32052     },
32053     
32054     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32055     {
32056         var pos = [];
32057         
32058         if(box[0].size == 'xs'){
32059             
32060             pos.push({
32061                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32062                 y : minY
32063             });
32064
32065             pos.push({
32066                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32067                 y : minY
32068             });
32069             
32070             pos.push({
32071                 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),
32072                 y : minY
32073             });
32074             
32075             pos.push({
32076                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32077                 y : minY + (this.unitWidth + this.gutter) * 1
32078             });
32079             
32080             return pos;
32081             
32082         }
32083         
32084         pos.push({
32085             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32086             y : minY
32087         });
32088         
32089         pos.push({
32090             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32091             y : minY + (this.unitWidth + this.gutter) * 2
32092         });
32093         
32094         pos.push({
32095             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32096             y : minY + (this.unitWidth + this.gutter) * 2
32097         });
32098         
32099         pos.push({
32100             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),
32101             y : minY + (this.unitWidth + this.gutter) * 2
32102         });
32103
32104         return pos;
32105         
32106     },
32107     
32108     /**
32109     * remove a Masonry Brick
32110     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32111     */
32112     removeBrick : function(brick_id)
32113     {
32114         if (!brick_id) {
32115             return;
32116         }
32117         
32118         for (var i = 0; i<this.bricks.length; i++) {
32119             if (this.bricks[i].id == brick_id) {
32120                 this.bricks.splice(i,1);
32121                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32122                 this.initial();
32123             }
32124         }
32125     },
32126     
32127     /**
32128     * adds a Masonry Brick
32129     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32130     */
32131     addBrick : function(cfg)
32132     {
32133         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32134         //this.register(cn);
32135         cn.parentId = this.id;
32136         cn.render(this.el);
32137         return cn;
32138     },
32139     
32140     /**
32141     * register a Masonry Brick
32142     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32143     */
32144     
32145     register : function(brick)
32146     {
32147         this.bricks.push(brick);
32148         brick.masonryId = this.id;
32149     },
32150     
32151     /**
32152     * clear all the Masonry Brick
32153     */
32154     clearAll : function()
32155     {
32156         this.bricks = [];
32157         //this.getChildContainer().dom.innerHTML = "";
32158         this.el.dom.innerHTML = '';
32159     },
32160     
32161     getSelected : function()
32162     {
32163         if (!this.selectedBrick) {
32164             return false;
32165         }
32166         
32167         return this.selectedBrick;
32168     }
32169 });
32170
32171 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32172     
32173     groups: {},
32174      /**
32175     * register a Masonry Layout
32176     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32177     */
32178     
32179     register : function(layout)
32180     {
32181         this.groups[layout.id] = layout;
32182     },
32183     /**
32184     * fetch a  Masonry Layout based on the masonry layout ID
32185     * @param {string} the masonry layout to add
32186     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32187     */
32188     
32189     get: function(layout_id) {
32190         if (typeof(this.groups[layout_id]) == 'undefined') {
32191             return false;
32192         }
32193         return this.groups[layout_id] ;
32194     }
32195     
32196     
32197     
32198 });
32199
32200  
32201
32202  /**
32203  *
32204  * This is based on 
32205  * http://masonry.desandro.com
32206  *
32207  * The idea is to render all the bricks based on vertical width...
32208  *
32209  * The original code extends 'outlayer' - we might need to use that....
32210  * 
32211  */
32212
32213
32214 /**
32215  * @class Roo.bootstrap.LayoutMasonryAuto
32216  * @extends Roo.bootstrap.Component
32217  * Bootstrap Layout Masonry class
32218  * 
32219  * @constructor
32220  * Create a new Element
32221  * @param {Object} config The config object
32222  */
32223
32224 Roo.bootstrap.LayoutMasonryAuto = function(config){
32225     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32226 };
32227
32228 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32229     
32230       /**
32231      * @cfg {Boolean} isFitWidth  - resize the width..
32232      */   
32233     isFitWidth : false,  // options..
32234     /**
32235      * @cfg {Boolean} isOriginLeft = left align?
32236      */   
32237     isOriginLeft : true,
32238     /**
32239      * @cfg {Boolean} isOriginTop = top align?
32240      */   
32241     isOriginTop : false,
32242     /**
32243      * @cfg {Boolean} isLayoutInstant = no animation?
32244      */   
32245     isLayoutInstant : false, // needed?
32246     /**
32247      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32248      */   
32249     isResizingContainer : true,
32250     /**
32251      * @cfg {Number} columnWidth  width of the columns 
32252      */   
32253     
32254     columnWidth : 0,
32255     
32256     /**
32257      * @cfg {Number} maxCols maximum number of columns
32258      */   
32259     
32260     maxCols: 0,
32261     /**
32262      * @cfg {Number} padHeight padding below box..
32263      */   
32264     
32265     padHeight : 10, 
32266     
32267     /**
32268      * @cfg {Boolean} isAutoInitial defalut true
32269      */   
32270     
32271     isAutoInitial : true, 
32272     
32273     // private?
32274     gutter : 0,
32275     
32276     containerWidth: 0,
32277     initialColumnWidth : 0,
32278     currentSize : null,
32279     
32280     colYs : null, // array.
32281     maxY : 0,
32282     padWidth: 10,
32283     
32284     
32285     tag: 'div',
32286     cls: '',
32287     bricks: null, //CompositeElement
32288     cols : 0, // array?
32289     // element : null, // wrapped now this.el
32290     _isLayoutInited : null, 
32291     
32292     
32293     getAutoCreate : function(){
32294         
32295         var cfg = {
32296             tag: this.tag,
32297             cls: 'blog-masonary-wrapper ' + this.cls,
32298             cn : {
32299                 cls : 'mas-boxes masonary'
32300             }
32301         };
32302         
32303         return cfg;
32304     },
32305     
32306     getChildContainer: function( )
32307     {
32308         if (this.boxesEl) {
32309             return this.boxesEl;
32310         }
32311         
32312         this.boxesEl = this.el.select('.mas-boxes').first();
32313         
32314         return this.boxesEl;
32315     },
32316     
32317     
32318     initEvents : function()
32319     {
32320         var _this = this;
32321         
32322         if(this.isAutoInitial){
32323             Roo.log('hook children rendered');
32324             this.on('childrenrendered', function() {
32325                 Roo.log('children rendered');
32326                 _this.initial();
32327             } ,this);
32328         }
32329         
32330     },
32331     
32332     initial : function()
32333     {
32334         this.reloadItems();
32335
32336         this.currentSize = this.el.getBox(true);
32337
32338         /// was window resize... - let's see if this works..
32339         Roo.EventManager.onWindowResize(this.resize, this); 
32340
32341         if(!this.isAutoInitial){
32342             this.layout();
32343             return;
32344         }
32345         
32346         this.layout.defer(500,this);
32347     },
32348     
32349     reloadItems: function()
32350     {
32351         this.bricks = this.el.select('.masonry-brick', true);
32352         
32353         this.bricks.each(function(b) {
32354             //Roo.log(b.getSize());
32355             if (!b.attr('originalwidth')) {
32356                 b.attr('originalwidth',  b.getSize().width);
32357             }
32358             
32359         });
32360         
32361         Roo.log(this.bricks.elements.length);
32362     },
32363     
32364     resize : function()
32365     {
32366         Roo.log('resize');
32367         var cs = this.el.getBox(true);
32368         
32369         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32370             Roo.log("no change in with or X");
32371             return;
32372         }
32373         this.currentSize = cs;
32374         this.layout();
32375     },
32376     
32377     layout : function()
32378     {
32379          Roo.log('layout');
32380         this._resetLayout();
32381         //this._manageStamps();
32382       
32383         // don't animate first layout
32384         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32385         this.layoutItems( isInstant );
32386       
32387         // flag for initalized
32388         this._isLayoutInited = true;
32389     },
32390     
32391     layoutItems : function( isInstant )
32392     {
32393         //var items = this._getItemsForLayout( this.items );
32394         // original code supports filtering layout items.. we just ignore it..
32395         
32396         this._layoutItems( this.bricks , isInstant );
32397       
32398         this._postLayout();
32399     },
32400     _layoutItems : function ( items , isInstant)
32401     {
32402        //this.fireEvent( 'layout', this, items );
32403     
32404
32405         if ( !items || !items.elements.length ) {
32406           // no items, emit event with empty array
32407             return;
32408         }
32409
32410         var queue = [];
32411         items.each(function(item) {
32412             Roo.log("layout item");
32413             Roo.log(item);
32414             // get x/y object from method
32415             var position = this._getItemLayoutPosition( item );
32416             // enqueue
32417             position.item = item;
32418             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32419             queue.push( position );
32420         }, this);
32421       
32422         this._processLayoutQueue( queue );
32423     },
32424     /** Sets position of item in DOM
32425     * @param {Element} item
32426     * @param {Number} x - horizontal position
32427     * @param {Number} y - vertical position
32428     * @param {Boolean} isInstant - disables transitions
32429     */
32430     _processLayoutQueue : function( queue )
32431     {
32432         for ( var i=0, len = queue.length; i < len; i++ ) {
32433             var obj = queue[i];
32434             obj.item.position('absolute');
32435             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32436         }
32437     },
32438       
32439     
32440     /**
32441     * Any logic you want to do after each layout,
32442     * i.e. size the container
32443     */
32444     _postLayout : function()
32445     {
32446         this.resizeContainer();
32447     },
32448     
32449     resizeContainer : function()
32450     {
32451         if ( !this.isResizingContainer ) {
32452             return;
32453         }
32454         var size = this._getContainerSize();
32455         if ( size ) {
32456             this.el.setSize(size.width,size.height);
32457             this.boxesEl.setSize(size.width,size.height);
32458         }
32459     },
32460     
32461     
32462     
32463     _resetLayout : function()
32464     {
32465         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32466         this.colWidth = this.el.getWidth();
32467         //this.gutter = this.el.getWidth(); 
32468         
32469         this.measureColumns();
32470
32471         // reset column Y
32472         var i = this.cols;
32473         this.colYs = [];
32474         while (i--) {
32475             this.colYs.push( 0 );
32476         }
32477     
32478         this.maxY = 0;
32479     },
32480
32481     measureColumns : function()
32482     {
32483         this.getContainerWidth();
32484       // if columnWidth is 0, default to outerWidth of first item
32485         if ( !this.columnWidth ) {
32486             var firstItem = this.bricks.first();
32487             Roo.log(firstItem);
32488             this.columnWidth  = this.containerWidth;
32489             if (firstItem && firstItem.attr('originalwidth') ) {
32490                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32491             }
32492             // columnWidth fall back to item of first element
32493             Roo.log("set column width?");
32494                         this.initialColumnWidth = this.columnWidth  ;
32495
32496             // if first elem has no width, default to size of container
32497             
32498         }
32499         
32500         
32501         if (this.initialColumnWidth) {
32502             this.columnWidth = this.initialColumnWidth;
32503         }
32504         
32505         
32506             
32507         // column width is fixed at the top - however if container width get's smaller we should
32508         // reduce it...
32509         
32510         // this bit calcs how man columns..
32511             
32512         var columnWidth = this.columnWidth += this.gutter;
32513       
32514         // calculate columns
32515         var containerWidth = this.containerWidth + this.gutter;
32516         
32517         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32518         // fix rounding errors, typically with gutters
32519         var excess = columnWidth - containerWidth % columnWidth;
32520         
32521         
32522         // if overshoot is less than a pixel, round up, otherwise floor it
32523         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32524         cols = Math[ mathMethod ]( cols );
32525         this.cols = Math.max( cols, 1 );
32526         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32527         
32528          // padding positioning..
32529         var totalColWidth = this.cols * this.columnWidth;
32530         var padavail = this.containerWidth - totalColWidth;
32531         // so for 2 columns - we need 3 'pads'
32532         
32533         var padNeeded = (1+this.cols) * this.padWidth;
32534         
32535         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32536         
32537         this.columnWidth += padExtra
32538         //this.padWidth = Math.floor(padavail /  ( this.cols));
32539         
32540         // adjust colum width so that padding is fixed??
32541         
32542         // we have 3 columns ... total = width * 3
32543         // we have X left over... that should be used by 
32544         
32545         //if (this.expandC) {
32546             
32547         //}
32548         
32549         
32550         
32551     },
32552     
32553     getContainerWidth : function()
32554     {
32555        /* // container is parent if fit width
32556         var container = this.isFitWidth ? this.element.parentNode : this.element;
32557         // check that this.size and size are there
32558         // IE8 triggers resize on body size change, so they might not be
32559         
32560         var size = getSize( container );  //FIXME
32561         this.containerWidth = size && size.innerWidth; //FIXME
32562         */
32563          
32564         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32565         
32566     },
32567     
32568     _getItemLayoutPosition : function( item )  // what is item?
32569     {
32570         // we resize the item to our columnWidth..
32571       
32572         item.setWidth(this.columnWidth);
32573         item.autoBoxAdjust  = false;
32574         
32575         var sz = item.getSize();
32576  
32577         // how many columns does this brick span
32578         var remainder = this.containerWidth % this.columnWidth;
32579         
32580         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32581         // round if off by 1 pixel, otherwise use ceil
32582         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32583         colSpan = Math.min( colSpan, this.cols );
32584         
32585         // normally this should be '1' as we dont' currently allow multi width columns..
32586         
32587         var colGroup = this._getColGroup( colSpan );
32588         // get the minimum Y value from the columns
32589         var minimumY = Math.min.apply( Math, colGroup );
32590         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32591         
32592         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32593          
32594         // position the brick
32595         var position = {
32596             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32597             y: this.currentSize.y + minimumY + this.padHeight
32598         };
32599         
32600         Roo.log(position);
32601         // apply setHeight to necessary columns
32602         var setHeight = minimumY + sz.height + this.padHeight;
32603         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32604         
32605         var setSpan = this.cols + 1 - colGroup.length;
32606         for ( var i = 0; i < setSpan; i++ ) {
32607           this.colYs[ shortColIndex + i ] = setHeight ;
32608         }
32609       
32610         return position;
32611     },
32612     
32613     /**
32614      * @param {Number} colSpan - number of columns the element spans
32615      * @returns {Array} colGroup
32616      */
32617     _getColGroup : function( colSpan )
32618     {
32619         if ( colSpan < 2 ) {
32620           // if brick spans only one column, use all the column Ys
32621           return this.colYs;
32622         }
32623       
32624         var colGroup = [];
32625         // how many different places could this brick fit horizontally
32626         var groupCount = this.cols + 1 - colSpan;
32627         // for each group potential horizontal position
32628         for ( var i = 0; i < groupCount; i++ ) {
32629           // make an array of colY values for that one group
32630           var groupColYs = this.colYs.slice( i, i + colSpan );
32631           // and get the max value of the array
32632           colGroup[i] = Math.max.apply( Math, groupColYs );
32633         }
32634         return colGroup;
32635     },
32636     /*
32637     _manageStamp : function( stamp )
32638     {
32639         var stampSize =  stamp.getSize();
32640         var offset = stamp.getBox();
32641         // get the columns that this stamp affects
32642         var firstX = this.isOriginLeft ? offset.x : offset.right;
32643         var lastX = firstX + stampSize.width;
32644         var firstCol = Math.floor( firstX / this.columnWidth );
32645         firstCol = Math.max( 0, firstCol );
32646         
32647         var lastCol = Math.floor( lastX / this.columnWidth );
32648         // lastCol should not go over if multiple of columnWidth #425
32649         lastCol -= lastX % this.columnWidth ? 0 : 1;
32650         lastCol = Math.min( this.cols - 1, lastCol );
32651         
32652         // set colYs to bottom of the stamp
32653         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32654             stampSize.height;
32655             
32656         for ( var i = firstCol; i <= lastCol; i++ ) {
32657           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32658         }
32659     },
32660     */
32661     
32662     _getContainerSize : function()
32663     {
32664         this.maxY = Math.max.apply( Math, this.colYs );
32665         var size = {
32666             height: this.maxY
32667         };
32668       
32669         if ( this.isFitWidth ) {
32670             size.width = this._getContainerFitWidth();
32671         }
32672       
32673         return size;
32674     },
32675     
32676     _getContainerFitWidth : function()
32677     {
32678         var unusedCols = 0;
32679         // count unused columns
32680         var i = this.cols;
32681         while ( --i ) {
32682           if ( this.colYs[i] !== 0 ) {
32683             break;
32684           }
32685           unusedCols++;
32686         }
32687         // fit container to columns that have been used
32688         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32689     },
32690     
32691     needsResizeLayout : function()
32692     {
32693         var previousWidth = this.containerWidth;
32694         this.getContainerWidth();
32695         return previousWidth !== this.containerWidth;
32696     }
32697  
32698 });
32699
32700  
32701
32702  /*
32703  * - LGPL
32704  *
32705  * element
32706  * 
32707  */
32708
32709 /**
32710  * @class Roo.bootstrap.MasonryBrick
32711  * @extends Roo.bootstrap.Component
32712  * Bootstrap MasonryBrick class
32713  * 
32714  * @constructor
32715  * Create a new MasonryBrick
32716  * @param {Object} config The config object
32717  */
32718
32719 Roo.bootstrap.MasonryBrick = function(config){
32720     
32721     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32722     
32723     Roo.bootstrap.MasonryBrick.register(this);
32724     
32725     this.addEvents({
32726         // raw events
32727         /**
32728          * @event click
32729          * When a MasonryBrick is clcik
32730          * @param {Roo.bootstrap.MasonryBrick} this
32731          * @param {Roo.EventObject} e
32732          */
32733         "click" : true
32734     });
32735 };
32736
32737 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32738     
32739     /**
32740      * @cfg {String} title
32741      */   
32742     title : '',
32743     /**
32744      * @cfg {String} html
32745      */   
32746     html : '',
32747     /**
32748      * @cfg {String} bgimage
32749      */   
32750     bgimage : '',
32751     /**
32752      * @cfg {String} videourl
32753      */   
32754     videourl : '',
32755     /**
32756      * @cfg {String} cls
32757      */   
32758     cls : '',
32759     /**
32760      * @cfg {String} href
32761      */   
32762     href : '',
32763     /**
32764      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32765      */   
32766     size : 'xs',
32767     
32768     /**
32769      * @cfg {String} placetitle (center|bottom)
32770      */   
32771     placetitle : '',
32772     
32773     /**
32774      * @cfg {Boolean} isFitContainer defalut true
32775      */   
32776     isFitContainer : true, 
32777     
32778     /**
32779      * @cfg {Boolean} preventDefault defalut false
32780      */   
32781     preventDefault : false, 
32782     
32783     /**
32784      * @cfg {Boolean} inverse defalut false
32785      */   
32786     maskInverse : false, 
32787     
32788     getAutoCreate : function()
32789     {
32790         if(!this.isFitContainer){
32791             return this.getSplitAutoCreate();
32792         }
32793         
32794         var cls = 'masonry-brick masonry-brick-full';
32795         
32796         if(this.href.length){
32797             cls += ' masonry-brick-link';
32798         }
32799         
32800         if(this.bgimage.length){
32801             cls += ' masonry-brick-image';
32802         }
32803         
32804         if(this.maskInverse){
32805             cls += ' mask-inverse';
32806         }
32807         
32808         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32809             cls += ' enable-mask';
32810         }
32811         
32812         if(this.size){
32813             cls += ' masonry-' + this.size + '-brick';
32814         }
32815         
32816         if(this.placetitle.length){
32817             
32818             switch (this.placetitle) {
32819                 case 'center' :
32820                     cls += ' masonry-center-title';
32821                     break;
32822                 case 'bottom' :
32823                     cls += ' masonry-bottom-title';
32824                     break;
32825                 default:
32826                     break;
32827             }
32828             
32829         } else {
32830             if(!this.html.length && !this.bgimage.length){
32831                 cls += ' masonry-center-title';
32832             }
32833
32834             if(!this.html.length && this.bgimage.length){
32835                 cls += ' masonry-bottom-title';
32836             }
32837         }
32838         
32839         if(this.cls){
32840             cls += ' ' + this.cls;
32841         }
32842         
32843         var cfg = {
32844             tag: (this.href.length) ? 'a' : 'div',
32845             cls: cls,
32846             cn: [
32847                 {
32848                     tag: 'div',
32849                     cls: 'masonry-brick-mask'
32850                 },
32851                 {
32852                     tag: 'div',
32853                     cls: 'masonry-brick-paragraph',
32854                     cn: []
32855                 }
32856             ]
32857         };
32858         
32859         if(this.href.length){
32860             cfg.href = this.href;
32861         }
32862         
32863         var cn = cfg.cn[1].cn;
32864         
32865         if(this.title.length){
32866             cn.push({
32867                 tag: 'h4',
32868                 cls: 'masonry-brick-title',
32869                 html: this.title
32870             });
32871         }
32872         
32873         if(this.html.length){
32874             cn.push({
32875                 tag: 'p',
32876                 cls: 'masonry-brick-text',
32877                 html: this.html
32878             });
32879         }
32880         
32881         if (!this.title.length && !this.html.length) {
32882             cfg.cn[1].cls += ' hide';
32883         }
32884         
32885         if(this.bgimage.length){
32886             cfg.cn.push({
32887                 tag: 'img',
32888                 cls: 'masonry-brick-image-view',
32889                 src: this.bgimage
32890             });
32891         }
32892         
32893         if(this.videourl.length){
32894             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32895             // youtube support only?
32896             cfg.cn.push({
32897                 tag: 'iframe',
32898                 cls: 'masonry-brick-image-view',
32899                 src: vurl,
32900                 frameborder : 0,
32901                 allowfullscreen : true
32902             });
32903         }
32904         
32905         return cfg;
32906         
32907     },
32908     
32909     getSplitAutoCreate : function()
32910     {
32911         var cls = 'masonry-brick masonry-brick-split';
32912         
32913         if(this.href.length){
32914             cls += ' masonry-brick-link';
32915         }
32916         
32917         if(this.bgimage.length){
32918             cls += ' masonry-brick-image';
32919         }
32920         
32921         if(this.size){
32922             cls += ' masonry-' + this.size + '-brick';
32923         }
32924         
32925         switch (this.placetitle) {
32926             case 'center' :
32927                 cls += ' masonry-center-title';
32928                 break;
32929             case 'bottom' :
32930                 cls += ' masonry-bottom-title';
32931                 break;
32932             default:
32933                 if(!this.bgimage.length){
32934                     cls += ' masonry-center-title';
32935                 }
32936
32937                 if(this.bgimage.length){
32938                     cls += ' masonry-bottom-title';
32939                 }
32940                 break;
32941         }
32942         
32943         if(this.cls){
32944             cls += ' ' + this.cls;
32945         }
32946         
32947         var cfg = {
32948             tag: (this.href.length) ? 'a' : 'div',
32949             cls: cls,
32950             cn: [
32951                 {
32952                     tag: 'div',
32953                     cls: 'masonry-brick-split-head',
32954                     cn: [
32955                         {
32956                             tag: 'div',
32957                             cls: 'masonry-brick-paragraph',
32958                             cn: []
32959                         }
32960                     ]
32961                 },
32962                 {
32963                     tag: 'div',
32964                     cls: 'masonry-brick-split-body',
32965                     cn: []
32966                 }
32967             ]
32968         };
32969         
32970         if(this.href.length){
32971             cfg.href = this.href;
32972         }
32973         
32974         if(this.title.length){
32975             cfg.cn[0].cn[0].cn.push({
32976                 tag: 'h4',
32977                 cls: 'masonry-brick-title',
32978                 html: this.title
32979             });
32980         }
32981         
32982         if(this.html.length){
32983             cfg.cn[1].cn.push({
32984                 tag: 'p',
32985                 cls: 'masonry-brick-text',
32986                 html: this.html
32987             });
32988         }
32989
32990         if(this.bgimage.length){
32991             cfg.cn[0].cn.push({
32992                 tag: 'img',
32993                 cls: 'masonry-brick-image-view',
32994                 src: this.bgimage
32995             });
32996         }
32997         
32998         if(this.videourl.length){
32999             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33000             // youtube support only?
33001             cfg.cn[0].cn.cn.push({
33002                 tag: 'iframe',
33003                 cls: 'masonry-brick-image-view',
33004                 src: vurl,
33005                 frameborder : 0,
33006                 allowfullscreen : true
33007             });
33008         }
33009         
33010         return cfg;
33011     },
33012     
33013     initEvents: function() 
33014     {
33015         switch (this.size) {
33016             case 'xs' :
33017                 this.x = 1;
33018                 this.y = 1;
33019                 break;
33020             case 'sm' :
33021                 this.x = 2;
33022                 this.y = 2;
33023                 break;
33024             case 'md' :
33025             case 'md-left' :
33026             case 'md-right' :
33027                 this.x = 3;
33028                 this.y = 3;
33029                 break;
33030             case 'tall' :
33031                 this.x = 2;
33032                 this.y = 3;
33033                 break;
33034             case 'wide' :
33035                 this.x = 3;
33036                 this.y = 2;
33037                 break;
33038             case 'wide-thin' :
33039                 this.x = 3;
33040                 this.y = 1;
33041                 break;
33042                         
33043             default :
33044                 break;
33045         }
33046         
33047         if(Roo.isTouch){
33048             this.el.on('touchstart', this.onTouchStart, this);
33049             this.el.on('touchmove', this.onTouchMove, this);
33050             this.el.on('touchend', this.onTouchEnd, this);
33051             this.el.on('contextmenu', this.onContextMenu, this);
33052         } else {
33053             this.el.on('mouseenter'  ,this.enter, this);
33054             this.el.on('mouseleave', this.leave, this);
33055             this.el.on('click', this.onClick, this);
33056         }
33057         
33058         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33059             this.parent().bricks.push(this);   
33060         }
33061         
33062     },
33063     
33064     onClick: function(e, el)
33065     {
33066         var time = this.endTimer - this.startTimer;
33067         // Roo.log(e.preventDefault());
33068         if(Roo.isTouch){
33069             if(time > 1000){
33070                 e.preventDefault();
33071                 return;
33072             }
33073         }
33074         
33075         if(!this.preventDefault){
33076             return;
33077         }
33078         
33079         e.preventDefault();
33080         
33081         if (this.activeClass != '') {
33082             this.selectBrick();
33083         }
33084         
33085         this.fireEvent('click', this, e);
33086     },
33087     
33088     enter: function(e, el)
33089     {
33090         e.preventDefault();
33091         
33092         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33093             return;
33094         }
33095         
33096         if(this.bgimage.length && this.html.length){
33097             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33098         }
33099     },
33100     
33101     leave: function(e, el)
33102     {
33103         e.preventDefault();
33104         
33105         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33106             return;
33107         }
33108         
33109         if(this.bgimage.length && this.html.length){
33110             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33111         }
33112     },
33113     
33114     onTouchStart: function(e, el)
33115     {
33116 //        e.preventDefault();
33117         
33118         this.touchmoved = false;
33119         
33120         if(!this.isFitContainer){
33121             return;
33122         }
33123         
33124         if(!this.bgimage.length || !this.html.length){
33125             return;
33126         }
33127         
33128         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33129         
33130         this.timer = new Date().getTime();
33131         
33132     },
33133     
33134     onTouchMove: function(e, el)
33135     {
33136         this.touchmoved = true;
33137     },
33138     
33139     onContextMenu : function(e,el)
33140     {
33141         e.preventDefault();
33142         e.stopPropagation();
33143         return false;
33144     },
33145     
33146     onTouchEnd: function(e, el)
33147     {
33148 //        e.preventDefault();
33149         
33150         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33151         
33152             this.leave(e,el);
33153             
33154             return;
33155         }
33156         
33157         if(!this.bgimage.length || !this.html.length){
33158             
33159             if(this.href.length){
33160                 window.location.href = this.href;
33161             }
33162             
33163             return;
33164         }
33165         
33166         if(!this.isFitContainer){
33167             return;
33168         }
33169         
33170         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33171         
33172         window.location.href = this.href;
33173     },
33174     
33175     //selection on single brick only
33176     selectBrick : function() {
33177         
33178         if (!this.parentId) {
33179             return;
33180         }
33181         
33182         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33183         var index = m.selectedBrick.indexOf(this.id);
33184         
33185         if ( index > -1) {
33186             m.selectedBrick.splice(index,1);
33187             this.el.removeClass(this.activeClass);
33188             return;
33189         }
33190         
33191         for(var i = 0; i < m.selectedBrick.length; i++) {
33192             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33193             b.el.removeClass(b.activeClass);
33194         }
33195         
33196         m.selectedBrick = [];
33197         
33198         m.selectedBrick.push(this.id);
33199         this.el.addClass(this.activeClass);
33200         return;
33201     },
33202     
33203     isSelected : function(){
33204         return this.el.hasClass(this.activeClass);
33205         
33206     }
33207 });
33208
33209 Roo.apply(Roo.bootstrap.MasonryBrick, {
33210     
33211     //groups: {},
33212     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33213      /**
33214     * register a Masonry Brick
33215     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33216     */
33217     
33218     register : function(brick)
33219     {
33220         //this.groups[brick.id] = brick;
33221         this.groups.add(brick.id, brick);
33222     },
33223     /**
33224     * fetch a  masonry brick based on the masonry brick ID
33225     * @param {string} the masonry brick to add
33226     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33227     */
33228     
33229     get: function(brick_id) 
33230     {
33231         // if (typeof(this.groups[brick_id]) == 'undefined') {
33232         //     return false;
33233         // }
33234         // return this.groups[brick_id] ;
33235         
33236         if(this.groups.key(brick_id)) {
33237             return this.groups.key(brick_id);
33238         }
33239         
33240         return false;
33241     }
33242     
33243     
33244     
33245 });
33246
33247  /*
33248  * - LGPL
33249  *
33250  * element
33251  * 
33252  */
33253
33254 /**
33255  * @class Roo.bootstrap.Brick
33256  * @extends Roo.bootstrap.Component
33257  * Bootstrap Brick class
33258  * 
33259  * @constructor
33260  * Create a new Brick
33261  * @param {Object} config The config object
33262  */
33263
33264 Roo.bootstrap.Brick = function(config){
33265     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33266     
33267     this.addEvents({
33268         // raw events
33269         /**
33270          * @event click
33271          * When a Brick is click
33272          * @param {Roo.bootstrap.Brick} this
33273          * @param {Roo.EventObject} e
33274          */
33275         "click" : true
33276     });
33277 };
33278
33279 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33280     
33281     /**
33282      * @cfg {String} title
33283      */   
33284     title : '',
33285     /**
33286      * @cfg {String} html
33287      */   
33288     html : '',
33289     /**
33290      * @cfg {String} bgimage
33291      */   
33292     bgimage : '',
33293     /**
33294      * @cfg {String} cls
33295      */   
33296     cls : '',
33297     /**
33298      * @cfg {String} href
33299      */   
33300     href : '',
33301     /**
33302      * @cfg {String} video
33303      */   
33304     video : '',
33305     /**
33306      * @cfg {Boolean} square
33307      */   
33308     square : true,
33309     
33310     getAutoCreate : function()
33311     {
33312         var cls = 'roo-brick';
33313         
33314         if(this.href.length){
33315             cls += ' roo-brick-link';
33316         }
33317         
33318         if(this.bgimage.length){
33319             cls += ' roo-brick-image';
33320         }
33321         
33322         if(!this.html.length && !this.bgimage.length){
33323             cls += ' roo-brick-center-title';
33324         }
33325         
33326         if(!this.html.length && this.bgimage.length){
33327             cls += ' roo-brick-bottom-title';
33328         }
33329         
33330         if(this.cls){
33331             cls += ' ' + this.cls;
33332         }
33333         
33334         var cfg = {
33335             tag: (this.href.length) ? 'a' : 'div',
33336             cls: cls,
33337             cn: [
33338                 {
33339                     tag: 'div',
33340                     cls: 'roo-brick-paragraph',
33341                     cn: []
33342                 }
33343             ]
33344         };
33345         
33346         if(this.href.length){
33347             cfg.href = this.href;
33348         }
33349         
33350         var cn = cfg.cn[0].cn;
33351         
33352         if(this.title.length){
33353             cn.push({
33354                 tag: 'h4',
33355                 cls: 'roo-brick-title',
33356                 html: this.title
33357             });
33358         }
33359         
33360         if(this.html.length){
33361             cn.push({
33362                 tag: 'p',
33363                 cls: 'roo-brick-text',
33364                 html: this.html
33365             });
33366         } else {
33367             cn.cls += ' hide';
33368         }
33369         
33370         if(this.bgimage.length){
33371             cfg.cn.push({
33372                 tag: 'img',
33373                 cls: 'roo-brick-image-view',
33374                 src: this.bgimage
33375             });
33376         }
33377         
33378         return cfg;
33379     },
33380     
33381     initEvents: function() 
33382     {
33383         if(this.title.length || this.html.length){
33384             this.el.on('mouseenter'  ,this.enter, this);
33385             this.el.on('mouseleave', this.leave, this);
33386         }
33387         
33388         Roo.EventManager.onWindowResize(this.resize, this); 
33389         
33390         if(this.bgimage.length){
33391             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33392             this.imageEl.on('load', this.onImageLoad, this);
33393             return;
33394         }
33395         
33396         this.resize();
33397     },
33398     
33399     onImageLoad : function()
33400     {
33401         this.resize();
33402     },
33403     
33404     resize : function()
33405     {
33406         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33407         
33408         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33409         
33410         if(this.bgimage.length){
33411             var image = this.el.select('.roo-brick-image-view', true).first();
33412             
33413             image.setWidth(paragraph.getWidth());
33414             
33415             if(this.square){
33416                 image.setHeight(paragraph.getWidth());
33417             }
33418             
33419             this.el.setHeight(image.getHeight());
33420             paragraph.setHeight(image.getHeight());
33421             
33422         }
33423         
33424     },
33425     
33426     enter: function(e, el)
33427     {
33428         e.preventDefault();
33429         
33430         if(this.bgimage.length){
33431             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33432             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33433         }
33434     },
33435     
33436     leave: function(e, el)
33437     {
33438         e.preventDefault();
33439         
33440         if(this.bgimage.length){
33441             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33442             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33443         }
33444     }
33445     
33446 });
33447
33448  
33449
33450  /*
33451  * - LGPL
33452  *
33453  * Number field 
33454  */
33455
33456 /**
33457  * @class Roo.bootstrap.NumberField
33458  * @extends Roo.bootstrap.Input
33459  * Bootstrap NumberField class
33460  * 
33461  * 
33462  * 
33463  * 
33464  * @constructor
33465  * Create a new NumberField
33466  * @param {Object} config The config object
33467  */
33468
33469 Roo.bootstrap.NumberField = function(config){
33470     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33471 };
33472
33473 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33474     
33475     /**
33476      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33477      */
33478     allowDecimals : true,
33479     /**
33480      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33481      */
33482     decimalSeparator : ".",
33483     /**
33484      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33485      */
33486     decimalPrecision : 2,
33487     /**
33488      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33489      */
33490     allowNegative : true,
33491     
33492     /**
33493      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33494      */
33495     allowZero: true,
33496     /**
33497      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33498      */
33499     minValue : Number.NEGATIVE_INFINITY,
33500     /**
33501      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33502      */
33503     maxValue : Number.MAX_VALUE,
33504     /**
33505      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33506      */
33507     minText : "The minimum value for this field is {0}",
33508     /**
33509      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33510      */
33511     maxText : "The maximum value for this field is {0}",
33512     /**
33513      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33514      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33515      */
33516     nanText : "{0} is not a valid number",
33517     /**
33518      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33519      */
33520     thousandsDelimiter : false,
33521     /**
33522      * @cfg {String} valueAlign alignment of value
33523      */
33524     valueAlign : "left",
33525
33526     getAutoCreate : function()
33527     {
33528         var hiddenInput = {
33529             tag: 'input',
33530             type: 'hidden',
33531             id: Roo.id(),
33532             cls: 'hidden-number-input'
33533         };
33534         
33535         if (this.name) {
33536             hiddenInput.name = this.name;
33537         }
33538         
33539         this.name = '';
33540         
33541         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33542         
33543         this.name = hiddenInput.name;
33544         
33545         if(cfg.cn.length > 0) {
33546             cfg.cn.push(hiddenInput);
33547         }
33548         
33549         return cfg;
33550     },
33551
33552     // private
33553     initEvents : function()
33554     {   
33555         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33556         
33557         var allowed = "0123456789";
33558         
33559         if(this.allowDecimals){
33560             allowed += this.decimalSeparator;
33561         }
33562         
33563         if(this.allowNegative){
33564             allowed += "-";
33565         }
33566         
33567         if(this.thousandsDelimiter) {
33568             allowed += ",";
33569         }
33570         
33571         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33572         
33573         var keyPress = function(e){
33574             
33575             var k = e.getKey();
33576             
33577             var c = e.getCharCode();
33578             
33579             if(
33580                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33581                     allowed.indexOf(String.fromCharCode(c)) === -1
33582             ){
33583                 e.stopEvent();
33584                 return;
33585             }
33586             
33587             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33588                 return;
33589             }
33590             
33591             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33592                 e.stopEvent();
33593             }
33594         };
33595         
33596         this.el.on("keypress", keyPress, this);
33597     },
33598     
33599     validateValue : function(value)
33600     {
33601         
33602         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33603             return false;
33604         }
33605         
33606         var num = this.parseValue(value);
33607         
33608         if(isNaN(num)){
33609             this.markInvalid(String.format(this.nanText, value));
33610             return false;
33611         }
33612         
33613         if(num < this.minValue){
33614             this.markInvalid(String.format(this.minText, this.minValue));
33615             return false;
33616         }
33617         
33618         if(num > this.maxValue){
33619             this.markInvalid(String.format(this.maxText, this.maxValue));
33620             return false;
33621         }
33622         
33623         return true;
33624     },
33625
33626     getValue : function()
33627     {
33628         var v = this.hiddenEl().getValue();
33629         
33630         return this.fixPrecision(this.parseValue(v));
33631     },
33632
33633     parseValue : function(value)
33634     {
33635         if(this.thousandsDelimiter) {
33636             value += "";
33637             r = new RegExp(",", "g");
33638             value = value.replace(r, "");
33639         }
33640         
33641         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33642         return isNaN(value) ? '' : value;
33643     },
33644
33645     fixPrecision : function(value)
33646     {
33647         if(this.thousandsDelimiter) {
33648             value += "";
33649             r = new RegExp(",", "g");
33650             value = value.replace(r, "");
33651         }
33652         
33653         var nan = isNaN(value);
33654         
33655         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33656             return nan ? '' : value;
33657         }
33658         return parseFloat(value).toFixed(this.decimalPrecision);
33659     },
33660
33661     setValue : function(v)
33662     {
33663         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33664         
33665         this.value = v;
33666         
33667         if(this.rendered){
33668             
33669             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33670             
33671             this.inputEl().dom.value = (v == '') ? '' :
33672                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33673             
33674             if(!this.allowZero && v === '0') {
33675                 this.hiddenEl().dom.value = '';
33676                 this.inputEl().dom.value = '';
33677             }
33678             
33679             this.validate();
33680         }
33681     },
33682
33683     decimalPrecisionFcn : function(v)
33684     {
33685         return Math.floor(v);
33686     },
33687
33688     beforeBlur : function()
33689     {
33690         var v = this.parseValue(this.getRawValue());
33691         
33692         if(v || v === 0 || v === ''){
33693             this.setValue(v);
33694         }
33695     },
33696     
33697     hiddenEl : function()
33698     {
33699         return this.el.select('input.hidden-number-input',true).first();
33700     }
33701     
33702 });
33703
33704  
33705
33706 /*
33707 * Licence: LGPL
33708 */
33709
33710 /**
33711  * @class Roo.bootstrap.DocumentSlider
33712  * @extends Roo.bootstrap.Component
33713  * Bootstrap DocumentSlider class
33714  * 
33715  * @constructor
33716  * Create a new DocumentViewer
33717  * @param {Object} config The config object
33718  */
33719
33720 Roo.bootstrap.DocumentSlider = function(config){
33721     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33722     
33723     this.files = [];
33724     
33725     this.addEvents({
33726         /**
33727          * @event initial
33728          * Fire after initEvent
33729          * @param {Roo.bootstrap.DocumentSlider} this
33730          */
33731         "initial" : true,
33732         /**
33733          * @event update
33734          * Fire after update
33735          * @param {Roo.bootstrap.DocumentSlider} this
33736          */
33737         "update" : true,
33738         /**
33739          * @event click
33740          * Fire after click
33741          * @param {Roo.bootstrap.DocumentSlider} this
33742          */
33743         "click" : true
33744     });
33745 };
33746
33747 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33748     
33749     files : false,
33750     
33751     indicator : 0,
33752     
33753     getAutoCreate : function()
33754     {
33755         var cfg = {
33756             tag : 'div',
33757             cls : 'roo-document-slider',
33758             cn : [
33759                 {
33760                     tag : 'div',
33761                     cls : 'roo-document-slider-header',
33762                     cn : [
33763                         {
33764                             tag : 'div',
33765                             cls : 'roo-document-slider-header-title'
33766                         }
33767                     ]
33768                 },
33769                 {
33770                     tag : 'div',
33771                     cls : 'roo-document-slider-body',
33772                     cn : [
33773                         {
33774                             tag : 'div',
33775                             cls : 'roo-document-slider-prev',
33776                             cn : [
33777                                 {
33778                                     tag : 'i',
33779                                     cls : 'fa fa-chevron-left'
33780                                 }
33781                             ]
33782                         },
33783                         {
33784                             tag : 'div',
33785                             cls : 'roo-document-slider-thumb',
33786                             cn : [
33787                                 {
33788                                     tag : 'img',
33789                                     cls : 'roo-document-slider-image'
33790                                 }
33791                             ]
33792                         },
33793                         {
33794                             tag : 'div',
33795                             cls : 'roo-document-slider-next',
33796                             cn : [
33797                                 {
33798                                     tag : 'i',
33799                                     cls : 'fa fa-chevron-right'
33800                                 }
33801                             ]
33802                         }
33803                     ]
33804                 }
33805             ]
33806         };
33807         
33808         return cfg;
33809     },
33810     
33811     initEvents : function()
33812     {
33813         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33814         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33815         
33816         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33817         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33818         
33819         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33820         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33821         
33822         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33823         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33824         
33825         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33826         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33827         
33828         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33829         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33830         
33831         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33832         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33833         
33834         this.thumbEl.on('click', this.onClick, this);
33835         
33836         this.prevIndicator.on('click', this.prev, this);
33837         
33838         this.nextIndicator.on('click', this.next, this);
33839         
33840     },
33841     
33842     initial : function()
33843     {
33844         if(this.files.length){
33845             this.indicator = 1;
33846             this.update()
33847         }
33848         
33849         this.fireEvent('initial', this);
33850     },
33851     
33852     update : function()
33853     {
33854         this.imageEl.attr('src', this.files[this.indicator - 1]);
33855         
33856         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33857         
33858         this.prevIndicator.show();
33859         
33860         if(this.indicator == 1){
33861             this.prevIndicator.hide();
33862         }
33863         
33864         this.nextIndicator.show();
33865         
33866         if(this.indicator == this.files.length){
33867             this.nextIndicator.hide();
33868         }
33869         
33870         this.thumbEl.scrollTo('top');
33871         
33872         this.fireEvent('update', this);
33873     },
33874     
33875     onClick : function(e)
33876     {
33877         e.preventDefault();
33878         
33879         this.fireEvent('click', this);
33880     },
33881     
33882     prev : function(e)
33883     {
33884         e.preventDefault();
33885         
33886         this.indicator = Math.max(1, this.indicator - 1);
33887         
33888         this.update();
33889     },
33890     
33891     next : function(e)
33892     {
33893         e.preventDefault();
33894         
33895         this.indicator = Math.min(this.files.length, this.indicator + 1);
33896         
33897         this.update();
33898     }
33899 });
33900 /*
33901  * - LGPL
33902  *
33903  * RadioSet
33904  *
33905  *
33906  */
33907
33908 /**
33909  * @class Roo.bootstrap.RadioSet
33910  * @extends Roo.bootstrap.Input
33911  * Bootstrap RadioSet class
33912  * @cfg {String} indicatorpos (left|right) default left
33913  * @cfg {Boolean} inline (true|false) inline the element (default true)
33914  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33915  * @constructor
33916  * Create a new RadioSet
33917  * @param {Object} config The config object
33918  */
33919
33920 Roo.bootstrap.RadioSet = function(config){
33921     
33922     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33923     
33924     this.radioes = [];
33925     
33926     Roo.bootstrap.RadioSet.register(this);
33927     
33928     this.addEvents({
33929         /**
33930         * @event check
33931         * Fires when the element is checked or unchecked.
33932         * @param {Roo.bootstrap.RadioSet} this This radio
33933         * @param {Roo.bootstrap.Radio} item The checked item
33934         */
33935        check : true,
33936        /**
33937         * @event click
33938         * Fires when the element is click.
33939         * @param {Roo.bootstrap.RadioSet} this This radio set
33940         * @param {Roo.bootstrap.Radio} item The checked item
33941         * @param {Roo.EventObject} e The event object
33942         */
33943        click : true
33944     });
33945     
33946 };
33947
33948 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33949
33950     radioes : false,
33951     
33952     inline : true,
33953     
33954     weight : '',
33955     
33956     indicatorpos : 'left',
33957     
33958     getAutoCreate : function()
33959     {
33960         var label = {
33961             tag : 'label',
33962             cls : 'roo-radio-set-label',
33963             cn : [
33964                 {
33965                     tag : 'span',
33966                     html : this.fieldLabel
33967                 }
33968             ]
33969         };
33970         
33971         if(this.indicatorpos == 'left'){
33972             label.cn.unshift({
33973                 tag : 'i',
33974                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33975                 tooltip : 'This field is required'
33976             });
33977         } else {
33978             label.cn.push({
33979                 tag : 'i',
33980                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33981                 tooltip : 'This field is required'
33982             });
33983         }
33984         
33985         var items = {
33986             tag : 'div',
33987             cls : 'roo-radio-set-items'
33988         };
33989         
33990         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33991         
33992         if (align === 'left' && this.fieldLabel.length) {
33993             
33994             items = {
33995                 cls : "roo-radio-set-right", 
33996                 cn: [
33997                     items
33998                 ]
33999             };
34000             
34001             if(this.labelWidth > 12){
34002                 label.style = "width: " + this.labelWidth + 'px';
34003             }
34004             
34005             if(this.labelWidth < 13 && this.labelmd == 0){
34006                 this.labelmd = this.labelWidth;
34007             }
34008             
34009             if(this.labellg > 0){
34010                 label.cls += ' col-lg-' + this.labellg;
34011                 items.cls += ' col-lg-' + (12 - this.labellg);
34012             }
34013             
34014             if(this.labelmd > 0){
34015                 label.cls += ' col-md-' + this.labelmd;
34016                 items.cls += ' col-md-' + (12 - this.labelmd);
34017             }
34018             
34019             if(this.labelsm > 0){
34020                 label.cls += ' col-sm-' + this.labelsm;
34021                 items.cls += ' col-sm-' + (12 - this.labelsm);
34022             }
34023             
34024             if(this.labelxs > 0){
34025                 label.cls += ' col-xs-' + this.labelxs;
34026                 items.cls += ' col-xs-' + (12 - this.labelxs);
34027             }
34028         }
34029         
34030         var cfg = {
34031             tag : 'div',
34032             cls : 'roo-radio-set',
34033             cn : [
34034                 {
34035                     tag : 'input',
34036                     cls : 'roo-radio-set-input',
34037                     type : 'hidden',
34038                     name : this.name,
34039                     value : this.value ? this.value :  ''
34040                 },
34041                 label,
34042                 items
34043             ]
34044         };
34045         
34046         if(this.weight.length){
34047             cfg.cls += ' roo-radio-' + this.weight;
34048         }
34049         
34050         if(this.inline) {
34051             cfg.cls += ' roo-radio-set-inline';
34052         }
34053         
34054         var settings=this;
34055         ['xs','sm','md','lg'].map(function(size){
34056             if (settings[size]) {
34057                 cfg.cls += ' col-' + size + '-' + settings[size];
34058             }
34059         });
34060         
34061         return cfg;
34062         
34063     },
34064
34065     initEvents : function()
34066     {
34067         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34068         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34069         
34070         if(!this.fieldLabel.length){
34071             this.labelEl.hide();
34072         }
34073         
34074         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34075         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34076         
34077         this.indicator = this.indicatorEl();
34078         
34079         if(this.indicator){
34080             this.indicator.addClass('invisible');
34081         }
34082         
34083         this.originalValue = this.getValue();
34084         
34085     },
34086     
34087     inputEl: function ()
34088     {
34089         return this.el.select('.roo-radio-set-input', true).first();
34090     },
34091     
34092     getChildContainer : function()
34093     {
34094         return this.itemsEl;
34095     },
34096     
34097     register : function(item)
34098     {
34099         this.radioes.push(item);
34100         
34101     },
34102     
34103     validate : function()
34104     {   
34105         if(this.getVisibilityEl().hasClass('hidden')){
34106             return true;
34107         }
34108         
34109         var valid = false;
34110         
34111         Roo.each(this.radioes, function(i){
34112             if(!i.checked){
34113                 return;
34114             }
34115             
34116             valid = true;
34117             return false;
34118         });
34119         
34120         if(this.allowBlank) {
34121             return true;
34122         }
34123         
34124         if(this.disabled || valid){
34125             this.markValid();
34126             return true;
34127         }
34128         
34129         this.markInvalid();
34130         return false;
34131         
34132     },
34133     
34134     markValid : function()
34135     {
34136         if(this.labelEl.isVisible(true)){
34137             this.indicatorEl().removeClass('visible');
34138             this.indicatorEl().addClass('invisible');
34139         }
34140         
34141         this.el.removeClass([this.invalidClass, this.validClass]);
34142         this.el.addClass(this.validClass);
34143         
34144         this.fireEvent('valid', this);
34145     },
34146     
34147     markInvalid : function(msg)
34148     {
34149         if(this.allowBlank || this.disabled){
34150             return;
34151         }
34152         
34153         if(this.labelEl.isVisible(true)){
34154             this.indicatorEl().removeClass('invisible');
34155             this.indicatorEl().addClass('visible');
34156         }
34157         
34158         this.el.removeClass([this.invalidClass, this.validClass]);
34159         this.el.addClass(this.invalidClass);
34160         
34161         this.fireEvent('invalid', this, msg);
34162         
34163     },
34164     
34165     setValue : function(v, suppressEvent)
34166     {   
34167         if(this.value === v){
34168             return;
34169         }
34170         
34171         this.value = v;
34172         
34173         if(this.rendered){
34174             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34175         }
34176         
34177         Roo.each(this.radioes, function(i){
34178             i.checked = false;
34179             i.el.removeClass('checked');
34180         });
34181         
34182         Roo.each(this.radioes, function(i){
34183             
34184             if(i.value === v || i.value.toString() === v.toString()){
34185                 i.checked = true;
34186                 i.el.addClass('checked');
34187                 
34188                 if(suppressEvent !== true){
34189                     this.fireEvent('check', this, i);
34190                 }
34191                 
34192                 return false;
34193             }
34194             
34195         }, this);
34196         
34197         this.validate();
34198     },
34199     
34200     clearInvalid : function(){
34201         
34202         if(!this.el || this.preventMark){
34203             return;
34204         }
34205         
34206         this.el.removeClass([this.invalidClass]);
34207         
34208         this.fireEvent('valid', this);
34209     }
34210     
34211 });
34212
34213 Roo.apply(Roo.bootstrap.RadioSet, {
34214     
34215     groups: {},
34216     
34217     register : function(set)
34218     {
34219         this.groups[set.name] = set;
34220     },
34221     
34222     get: function(name) 
34223     {
34224         if (typeof(this.groups[name]) == 'undefined') {
34225             return false;
34226         }
34227         
34228         return this.groups[name] ;
34229     }
34230     
34231 });
34232 /*
34233  * Based on:
34234  * Ext JS Library 1.1.1
34235  * Copyright(c) 2006-2007, Ext JS, LLC.
34236  *
34237  * Originally Released Under LGPL - original licence link has changed is not relivant.
34238  *
34239  * Fork - LGPL
34240  * <script type="text/javascript">
34241  */
34242
34243
34244 /**
34245  * @class Roo.bootstrap.SplitBar
34246  * @extends Roo.util.Observable
34247  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34248  * <br><br>
34249  * Usage:
34250  * <pre><code>
34251 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34252                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34253 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34254 split.minSize = 100;
34255 split.maxSize = 600;
34256 split.animate = true;
34257 split.on('moved', splitterMoved);
34258 </code></pre>
34259  * @constructor
34260  * Create a new SplitBar
34261  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34262  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34263  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34264  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34265                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34266                         position of the SplitBar).
34267  */
34268 Roo.bootstrap.SplitBar = function(cfg){
34269     
34270     /** @private */
34271     
34272     //{
34273     //  dragElement : elm
34274     //  resizingElement: el,
34275         // optional..
34276     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34277     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34278         // existingProxy ???
34279     //}
34280     
34281     this.el = Roo.get(cfg.dragElement, true);
34282     this.el.dom.unselectable = "on";
34283     /** @private */
34284     this.resizingEl = Roo.get(cfg.resizingElement, true);
34285
34286     /**
34287      * @private
34288      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34289      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34290      * @type Number
34291      */
34292     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34293     
34294     /**
34295      * The minimum size of the resizing element. (Defaults to 0)
34296      * @type Number
34297      */
34298     this.minSize = 0;
34299     
34300     /**
34301      * The maximum size of the resizing element. (Defaults to 2000)
34302      * @type Number
34303      */
34304     this.maxSize = 2000;
34305     
34306     /**
34307      * Whether to animate the transition to the new size
34308      * @type Boolean
34309      */
34310     this.animate = false;
34311     
34312     /**
34313      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34314      * @type Boolean
34315      */
34316     this.useShim = false;
34317     
34318     /** @private */
34319     this.shim = null;
34320     
34321     if(!cfg.existingProxy){
34322         /** @private */
34323         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34324     }else{
34325         this.proxy = Roo.get(cfg.existingProxy).dom;
34326     }
34327     /** @private */
34328     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34329     
34330     /** @private */
34331     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34332     
34333     /** @private */
34334     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34335     
34336     /** @private */
34337     this.dragSpecs = {};
34338     
34339     /**
34340      * @private The adapter to use to positon and resize elements
34341      */
34342     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34343     this.adapter.init(this);
34344     
34345     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34346         /** @private */
34347         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34348         this.el.addClass("roo-splitbar-h");
34349     }else{
34350         /** @private */
34351         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34352         this.el.addClass("roo-splitbar-v");
34353     }
34354     
34355     this.addEvents({
34356         /**
34357          * @event resize
34358          * Fires when the splitter is moved (alias for {@link #event-moved})
34359          * @param {Roo.bootstrap.SplitBar} this
34360          * @param {Number} newSize the new width or height
34361          */
34362         "resize" : true,
34363         /**
34364          * @event moved
34365          * Fires when the splitter is moved
34366          * @param {Roo.bootstrap.SplitBar} this
34367          * @param {Number} newSize the new width or height
34368          */
34369         "moved" : true,
34370         /**
34371          * @event beforeresize
34372          * Fires before the splitter is dragged
34373          * @param {Roo.bootstrap.SplitBar} this
34374          */
34375         "beforeresize" : true,
34376
34377         "beforeapply" : true
34378     });
34379
34380     Roo.util.Observable.call(this);
34381 };
34382
34383 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34384     onStartProxyDrag : function(x, y){
34385         this.fireEvent("beforeresize", this);
34386         if(!this.overlay){
34387             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34388             o.unselectable();
34389             o.enableDisplayMode("block");
34390             // all splitbars share the same overlay
34391             Roo.bootstrap.SplitBar.prototype.overlay = o;
34392         }
34393         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34394         this.overlay.show();
34395         Roo.get(this.proxy).setDisplayed("block");
34396         var size = this.adapter.getElementSize(this);
34397         this.activeMinSize = this.getMinimumSize();;
34398         this.activeMaxSize = this.getMaximumSize();;
34399         var c1 = size - this.activeMinSize;
34400         var c2 = Math.max(this.activeMaxSize - size, 0);
34401         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34402             this.dd.resetConstraints();
34403             this.dd.setXConstraint(
34404                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34405                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34406             );
34407             this.dd.setYConstraint(0, 0);
34408         }else{
34409             this.dd.resetConstraints();
34410             this.dd.setXConstraint(0, 0);
34411             this.dd.setYConstraint(
34412                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34413                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34414             );
34415          }
34416         this.dragSpecs.startSize = size;
34417         this.dragSpecs.startPoint = [x, y];
34418         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34419     },
34420     
34421     /** 
34422      * @private Called after the drag operation by the DDProxy
34423      */
34424     onEndProxyDrag : function(e){
34425         Roo.get(this.proxy).setDisplayed(false);
34426         var endPoint = Roo.lib.Event.getXY(e);
34427         if(this.overlay){
34428             this.overlay.hide();
34429         }
34430         var newSize;
34431         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34432             newSize = this.dragSpecs.startSize + 
34433                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34434                     endPoint[0] - this.dragSpecs.startPoint[0] :
34435                     this.dragSpecs.startPoint[0] - endPoint[0]
34436                 );
34437         }else{
34438             newSize = this.dragSpecs.startSize + 
34439                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34440                     endPoint[1] - this.dragSpecs.startPoint[1] :
34441                     this.dragSpecs.startPoint[1] - endPoint[1]
34442                 );
34443         }
34444         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34445         if(newSize != this.dragSpecs.startSize){
34446             if(this.fireEvent('beforeapply', this, newSize) !== false){
34447                 this.adapter.setElementSize(this, newSize);
34448                 this.fireEvent("moved", this, newSize);
34449                 this.fireEvent("resize", this, newSize);
34450             }
34451         }
34452     },
34453     
34454     /**
34455      * Get the adapter this SplitBar uses
34456      * @return The adapter object
34457      */
34458     getAdapter : function(){
34459         return this.adapter;
34460     },
34461     
34462     /**
34463      * Set the adapter this SplitBar uses
34464      * @param {Object} adapter A SplitBar adapter object
34465      */
34466     setAdapter : function(adapter){
34467         this.adapter = adapter;
34468         this.adapter.init(this);
34469     },
34470     
34471     /**
34472      * Gets the minimum size for the resizing element
34473      * @return {Number} The minimum size
34474      */
34475     getMinimumSize : function(){
34476         return this.minSize;
34477     },
34478     
34479     /**
34480      * Sets the minimum size for the resizing element
34481      * @param {Number} minSize The minimum size
34482      */
34483     setMinimumSize : function(minSize){
34484         this.minSize = minSize;
34485     },
34486     
34487     /**
34488      * Gets the maximum size for the resizing element
34489      * @return {Number} The maximum size
34490      */
34491     getMaximumSize : function(){
34492         return this.maxSize;
34493     },
34494     
34495     /**
34496      * Sets the maximum size for the resizing element
34497      * @param {Number} maxSize The maximum size
34498      */
34499     setMaximumSize : function(maxSize){
34500         this.maxSize = maxSize;
34501     },
34502     
34503     /**
34504      * Sets the initialize size for the resizing element
34505      * @param {Number} size The initial size
34506      */
34507     setCurrentSize : function(size){
34508         var oldAnimate = this.animate;
34509         this.animate = false;
34510         this.adapter.setElementSize(this, size);
34511         this.animate = oldAnimate;
34512     },
34513     
34514     /**
34515      * Destroy this splitbar. 
34516      * @param {Boolean} removeEl True to remove the element
34517      */
34518     destroy : function(removeEl){
34519         if(this.shim){
34520             this.shim.remove();
34521         }
34522         this.dd.unreg();
34523         this.proxy.parentNode.removeChild(this.proxy);
34524         if(removeEl){
34525             this.el.remove();
34526         }
34527     }
34528 });
34529
34530 /**
34531  * @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.
34532  */
34533 Roo.bootstrap.SplitBar.createProxy = function(dir){
34534     var proxy = new Roo.Element(document.createElement("div"));
34535     proxy.unselectable();
34536     var cls = 'roo-splitbar-proxy';
34537     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34538     document.body.appendChild(proxy.dom);
34539     return proxy.dom;
34540 };
34541
34542 /** 
34543  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34544  * Default Adapter. It assumes the splitter and resizing element are not positioned
34545  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34546  */
34547 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34548 };
34549
34550 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34551     // do nothing for now
34552     init : function(s){
34553     
34554     },
34555     /**
34556      * Called before drag operations to get the current size of the resizing element. 
34557      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34558      */
34559      getElementSize : function(s){
34560         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34561             return s.resizingEl.getWidth();
34562         }else{
34563             return s.resizingEl.getHeight();
34564         }
34565     },
34566     
34567     /**
34568      * Called after drag operations to set the size of the resizing element.
34569      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34570      * @param {Number} newSize The new size to set
34571      * @param {Function} onComplete A function to be invoked when resizing is complete
34572      */
34573     setElementSize : function(s, newSize, onComplete){
34574         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34575             if(!s.animate){
34576                 s.resizingEl.setWidth(newSize);
34577                 if(onComplete){
34578                     onComplete(s, newSize);
34579                 }
34580             }else{
34581                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34582             }
34583         }else{
34584             
34585             if(!s.animate){
34586                 s.resizingEl.setHeight(newSize);
34587                 if(onComplete){
34588                     onComplete(s, newSize);
34589                 }
34590             }else{
34591                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34592             }
34593         }
34594     }
34595 };
34596
34597 /** 
34598  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34599  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34600  * Adapter that  moves the splitter element to align with the resized sizing element. 
34601  * Used with an absolute positioned SplitBar.
34602  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34603  * document.body, make sure you assign an id to the body element.
34604  */
34605 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34606     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34607     this.container = Roo.get(container);
34608 };
34609
34610 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34611     init : function(s){
34612         this.basic.init(s);
34613     },
34614     
34615     getElementSize : function(s){
34616         return this.basic.getElementSize(s);
34617     },
34618     
34619     setElementSize : function(s, newSize, onComplete){
34620         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34621     },
34622     
34623     moveSplitter : function(s){
34624         var yes = Roo.bootstrap.SplitBar;
34625         switch(s.placement){
34626             case yes.LEFT:
34627                 s.el.setX(s.resizingEl.getRight());
34628                 break;
34629             case yes.RIGHT:
34630                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34631                 break;
34632             case yes.TOP:
34633                 s.el.setY(s.resizingEl.getBottom());
34634                 break;
34635             case yes.BOTTOM:
34636                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34637                 break;
34638         }
34639     }
34640 };
34641
34642 /**
34643  * Orientation constant - Create a vertical SplitBar
34644  * @static
34645  * @type Number
34646  */
34647 Roo.bootstrap.SplitBar.VERTICAL = 1;
34648
34649 /**
34650  * Orientation constant - Create a horizontal SplitBar
34651  * @static
34652  * @type Number
34653  */
34654 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34655
34656 /**
34657  * Placement constant - The resizing element is to the left of the splitter element
34658  * @static
34659  * @type Number
34660  */
34661 Roo.bootstrap.SplitBar.LEFT = 1;
34662
34663 /**
34664  * Placement constant - The resizing element is to the right of the splitter element
34665  * @static
34666  * @type Number
34667  */
34668 Roo.bootstrap.SplitBar.RIGHT = 2;
34669
34670 /**
34671  * Placement constant - The resizing element is positioned above the splitter element
34672  * @static
34673  * @type Number
34674  */
34675 Roo.bootstrap.SplitBar.TOP = 3;
34676
34677 /**
34678  * Placement constant - The resizing element is positioned under splitter element
34679  * @static
34680  * @type Number
34681  */
34682 Roo.bootstrap.SplitBar.BOTTOM = 4;
34683 Roo.namespace("Roo.bootstrap.layout");/*
34684  * Based on:
34685  * Ext JS Library 1.1.1
34686  * Copyright(c) 2006-2007, Ext JS, LLC.
34687  *
34688  * Originally Released Under LGPL - original licence link has changed is not relivant.
34689  *
34690  * Fork - LGPL
34691  * <script type="text/javascript">
34692  */
34693
34694 /**
34695  * @class Roo.bootstrap.layout.Manager
34696  * @extends Roo.bootstrap.Component
34697  * Base class for layout managers.
34698  */
34699 Roo.bootstrap.layout.Manager = function(config)
34700 {
34701     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34702
34703
34704
34705
34706
34707     /** false to disable window resize monitoring @type Boolean */
34708     this.monitorWindowResize = true;
34709     this.regions = {};
34710     this.addEvents({
34711         /**
34712          * @event layout
34713          * Fires when a layout is performed.
34714          * @param {Roo.LayoutManager} this
34715          */
34716         "layout" : true,
34717         /**
34718          * @event regionresized
34719          * Fires when the user resizes a region.
34720          * @param {Roo.LayoutRegion} region The resized region
34721          * @param {Number} newSize The new size (width for east/west, height for north/south)
34722          */
34723         "regionresized" : true,
34724         /**
34725          * @event regioncollapsed
34726          * Fires when a region is collapsed.
34727          * @param {Roo.LayoutRegion} region The collapsed region
34728          */
34729         "regioncollapsed" : true,
34730         /**
34731          * @event regionexpanded
34732          * Fires when a region is expanded.
34733          * @param {Roo.LayoutRegion} region The expanded region
34734          */
34735         "regionexpanded" : true
34736     });
34737     this.updating = false;
34738
34739     if (config.el) {
34740         this.el = Roo.get(config.el);
34741         this.initEvents();
34742     }
34743
34744 };
34745
34746 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34747
34748
34749     regions : null,
34750
34751     monitorWindowResize : true,
34752
34753
34754     updating : false,
34755
34756
34757     onRender : function(ct, position)
34758     {
34759         if(!this.el){
34760             this.el = Roo.get(ct);
34761             this.initEvents();
34762         }
34763         //this.fireEvent('render',this);
34764     },
34765
34766
34767     initEvents: function()
34768     {
34769
34770
34771         // ie scrollbar fix
34772         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34773             document.body.scroll = "no";
34774         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34775             this.el.position('relative');
34776         }
34777         this.id = this.el.id;
34778         this.el.addClass("roo-layout-container");
34779         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34780         if(this.el.dom != document.body ) {
34781             this.el.on('resize', this.layout,this);
34782             this.el.on('show', this.layout,this);
34783         }
34784
34785     },
34786
34787     /**
34788      * Returns true if this layout is currently being updated
34789      * @return {Boolean}
34790      */
34791     isUpdating : function(){
34792         return this.updating;
34793     },
34794
34795     /**
34796      * Suspend the LayoutManager from doing auto-layouts while
34797      * making multiple add or remove calls
34798      */
34799     beginUpdate : function(){
34800         this.updating = true;
34801     },
34802
34803     /**
34804      * Restore auto-layouts and optionally disable the manager from performing a layout
34805      * @param {Boolean} noLayout true to disable a layout update
34806      */
34807     endUpdate : function(noLayout){
34808         this.updating = false;
34809         if(!noLayout){
34810             this.layout();
34811         }
34812     },
34813
34814     layout: function(){
34815         // abstract...
34816     },
34817
34818     onRegionResized : function(region, newSize){
34819         this.fireEvent("regionresized", region, newSize);
34820         this.layout();
34821     },
34822
34823     onRegionCollapsed : function(region){
34824         this.fireEvent("regioncollapsed", region);
34825     },
34826
34827     onRegionExpanded : function(region){
34828         this.fireEvent("regionexpanded", region);
34829     },
34830
34831     /**
34832      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34833      * performs box-model adjustments.
34834      * @return {Object} The size as an object {width: (the width), height: (the height)}
34835      */
34836     getViewSize : function()
34837     {
34838         var size;
34839         if(this.el.dom != document.body){
34840             size = this.el.getSize();
34841         }else{
34842             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34843         }
34844         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34845         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34846         return size;
34847     },
34848
34849     /**
34850      * Returns the Element this layout is bound to.
34851      * @return {Roo.Element}
34852      */
34853     getEl : function(){
34854         return this.el;
34855     },
34856
34857     /**
34858      * Returns the specified region.
34859      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34860      * @return {Roo.LayoutRegion}
34861      */
34862     getRegion : function(target){
34863         return this.regions[target.toLowerCase()];
34864     },
34865
34866     onWindowResize : function(){
34867         if(this.monitorWindowResize){
34868             this.layout();
34869         }
34870     }
34871 });
34872 /*
34873  * Based on:
34874  * Ext JS Library 1.1.1
34875  * Copyright(c) 2006-2007, Ext JS, LLC.
34876  *
34877  * Originally Released Under LGPL - original licence link has changed is not relivant.
34878  *
34879  * Fork - LGPL
34880  * <script type="text/javascript">
34881  */
34882 /**
34883  * @class Roo.bootstrap.layout.Border
34884  * @extends Roo.bootstrap.layout.Manager
34885  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34886  * please see: examples/bootstrap/nested.html<br><br>
34887  
34888 <b>The container the layout is rendered into can be either the body element or any other element.
34889 If it is not the body element, the container needs to either be an absolute positioned element,
34890 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34891 the container size if it is not the body element.</b>
34892
34893 * @constructor
34894 * Create a new Border
34895 * @param {Object} config Configuration options
34896  */
34897 Roo.bootstrap.layout.Border = function(config){
34898     config = config || {};
34899     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34900     
34901     
34902     
34903     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34904         if(config[region]){
34905             config[region].region = region;
34906             this.addRegion(config[region]);
34907         }
34908     },this);
34909     
34910 };
34911
34912 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34913
34914 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34915     /**
34916      * Creates and adds a new region if it doesn't already exist.
34917      * @param {String} target The target region key (north, south, east, west or center).
34918      * @param {Object} config The regions config object
34919      * @return {BorderLayoutRegion} The new region
34920      */
34921     addRegion : function(config)
34922     {
34923         if(!this.regions[config.region]){
34924             var r = this.factory(config);
34925             this.bindRegion(r);
34926         }
34927         return this.regions[config.region];
34928     },
34929
34930     // private (kinda)
34931     bindRegion : function(r){
34932         this.regions[r.config.region] = r;
34933         
34934         r.on("visibilitychange",    this.layout, this);
34935         r.on("paneladded",          this.layout, this);
34936         r.on("panelremoved",        this.layout, this);
34937         r.on("invalidated",         this.layout, this);
34938         r.on("resized",             this.onRegionResized, this);
34939         r.on("collapsed",           this.onRegionCollapsed, this);
34940         r.on("expanded",            this.onRegionExpanded, this);
34941     },
34942
34943     /**
34944      * Performs a layout update.
34945      */
34946     layout : function()
34947     {
34948         if(this.updating) {
34949             return;
34950         }
34951         
34952         // render all the rebions if they have not been done alreayd?
34953         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34954             if(this.regions[region] && !this.regions[region].bodyEl){
34955                 this.regions[region].onRender(this.el)
34956             }
34957         },this);
34958         
34959         var size = this.getViewSize();
34960         var w = size.width;
34961         var h = size.height;
34962         var centerW = w;
34963         var centerH = h;
34964         var centerY = 0;
34965         var centerX = 0;
34966         //var x = 0, y = 0;
34967
34968         var rs = this.regions;
34969         var north = rs["north"];
34970         var south = rs["south"]; 
34971         var west = rs["west"];
34972         var east = rs["east"];
34973         var center = rs["center"];
34974         //if(this.hideOnLayout){ // not supported anymore
34975             //c.el.setStyle("display", "none");
34976         //}
34977         if(north && north.isVisible()){
34978             var b = north.getBox();
34979             var m = north.getMargins();
34980             b.width = w - (m.left+m.right);
34981             b.x = m.left;
34982             b.y = m.top;
34983             centerY = b.height + b.y + m.bottom;
34984             centerH -= centerY;
34985             north.updateBox(this.safeBox(b));
34986         }
34987         if(south && south.isVisible()){
34988             var b = south.getBox();
34989             var m = south.getMargins();
34990             b.width = w - (m.left+m.right);
34991             b.x = m.left;
34992             var totalHeight = (b.height + m.top + m.bottom);
34993             b.y = h - totalHeight + m.top;
34994             centerH -= totalHeight;
34995             south.updateBox(this.safeBox(b));
34996         }
34997         if(west && west.isVisible()){
34998             var b = west.getBox();
34999             var m = west.getMargins();
35000             b.height = centerH - (m.top+m.bottom);
35001             b.x = m.left;
35002             b.y = centerY + m.top;
35003             var totalWidth = (b.width + m.left + m.right);
35004             centerX += totalWidth;
35005             centerW -= totalWidth;
35006             west.updateBox(this.safeBox(b));
35007         }
35008         if(east && east.isVisible()){
35009             var b = east.getBox();
35010             var m = east.getMargins();
35011             b.height = centerH - (m.top+m.bottom);
35012             var totalWidth = (b.width + m.left + m.right);
35013             b.x = w - totalWidth + m.left;
35014             b.y = centerY + m.top;
35015             centerW -= totalWidth;
35016             east.updateBox(this.safeBox(b));
35017         }
35018         if(center){
35019             var m = center.getMargins();
35020             var centerBox = {
35021                 x: centerX + m.left,
35022                 y: centerY + m.top,
35023                 width: centerW - (m.left+m.right),
35024                 height: centerH - (m.top+m.bottom)
35025             };
35026             //if(this.hideOnLayout){
35027                 //center.el.setStyle("display", "block");
35028             //}
35029             center.updateBox(this.safeBox(centerBox));
35030         }
35031         this.el.repaint();
35032         this.fireEvent("layout", this);
35033     },
35034
35035     // private
35036     safeBox : function(box){
35037         box.width = Math.max(0, box.width);
35038         box.height = Math.max(0, box.height);
35039         return box;
35040     },
35041
35042     /**
35043      * Adds a ContentPanel (or subclass) to this layout.
35044      * @param {String} target The target region key (north, south, east, west or center).
35045      * @param {Roo.ContentPanel} panel The panel to add
35046      * @return {Roo.ContentPanel} The added panel
35047      */
35048     add : function(target, panel){
35049          
35050         target = target.toLowerCase();
35051         return this.regions[target].add(panel);
35052     },
35053
35054     /**
35055      * Remove a ContentPanel (or subclass) to this layout.
35056      * @param {String} target The target region key (north, south, east, west or center).
35057      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35058      * @return {Roo.ContentPanel} The removed panel
35059      */
35060     remove : function(target, panel){
35061         target = target.toLowerCase();
35062         return this.regions[target].remove(panel);
35063     },
35064
35065     /**
35066      * Searches all regions for a panel with the specified id
35067      * @param {String} panelId
35068      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35069      */
35070     findPanel : function(panelId){
35071         var rs = this.regions;
35072         for(var target in rs){
35073             if(typeof rs[target] != "function"){
35074                 var p = rs[target].getPanel(panelId);
35075                 if(p){
35076                     return p;
35077                 }
35078             }
35079         }
35080         return null;
35081     },
35082
35083     /**
35084      * Searches all regions for a panel with the specified id and activates (shows) it.
35085      * @param {String/ContentPanel} panelId The panels id or the panel itself
35086      * @return {Roo.ContentPanel} The shown panel or null
35087      */
35088     showPanel : function(panelId) {
35089       var rs = this.regions;
35090       for(var target in rs){
35091          var r = rs[target];
35092          if(typeof r != "function"){
35093             if(r.hasPanel(panelId)){
35094                return r.showPanel(panelId);
35095             }
35096          }
35097       }
35098       return null;
35099    },
35100
35101    /**
35102      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35103      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35104      */
35105    /*
35106     restoreState : function(provider){
35107         if(!provider){
35108             provider = Roo.state.Manager;
35109         }
35110         var sm = new Roo.LayoutStateManager();
35111         sm.init(this, provider);
35112     },
35113 */
35114  
35115  
35116     /**
35117      * Adds a xtype elements to the layout.
35118      * <pre><code>
35119
35120 layout.addxtype({
35121        xtype : 'ContentPanel',
35122        region: 'west',
35123        items: [ .... ]
35124    }
35125 );
35126
35127 layout.addxtype({
35128         xtype : 'NestedLayoutPanel',
35129         region: 'west',
35130         layout: {
35131            center: { },
35132            west: { }   
35133         },
35134         items : [ ... list of content panels or nested layout panels.. ]
35135    }
35136 );
35137 </code></pre>
35138      * @param {Object} cfg Xtype definition of item to add.
35139      */
35140     addxtype : function(cfg)
35141     {
35142         // basically accepts a pannel...
35143         // can accept a layout region..!?!?
35144         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35145         
35146         
35147         // theory?  children can only be panels??
35148         
35149         //if (!cfg.xtype.match(/Panel$/)) {
35150         //    return false;
35151         //}
35152         var ret = false;
35153         
35154         if (typeof(cfg.region) == 'undefined') {
35155             Roo.log("Failed to add Panel, region was not set");
35156             Roo.log(cfg);
35157             return false;
35158         }
35159         var region = cfg.region;
35160         delete cfg.region;
35161         
35162           
35163         var xitems = [];
35164         if (cfg.items) {
35165             xitems = cfg.items;
35166             delete cfg.items;
35167         }
35168         var nb = false;
35169         
35170         switch(cfg.xtype) 
35171         {
35172             case 'Content':  // ContentPanel (el, cfg)
35173             case 'Scroll':  // ContentPanel (el, cfg)
35174             case 'View': 
35175                 cfg.autoCreate = true;
35176                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35177                 //} else {
35178                 //    var el = this.el.createChild();
35179                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35180                 //}
35181                 
35182                 this.add(region, ret);
35183                 break;
35184             
35185             /*
35186             case 'TreePanel': // our new panel!
35187                 cfg.el = this.el.createChild();
35188                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35189                 this.add(region, ret);
35190                 break;
35191             */
35192             
35193             case 'Nest': 
35194                 // create a new Layout (which is  a Border Layout...
35195                 
35196                 var clayout = cfg.layout;
35197                 clayout.el  = this.el.createChild();
35198                 clayout.items   = clayout.items  || [];
35199                 
35200                 delete cfg.layout;
35201                 
35202                 // replace this exitems with the clayout ones..
35203                 xitems = clayout.items;
35204                  
35205                 // force background off if it's in center...
35206                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35207                     cfg.background = false;
35208                 }
35209                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35210                 
35211                 
35212                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35213                 //console.log('adding nested layout panel '  + cfg.toSource());
35214                 this.add(region, ret);
35215                 nb = {}; /// find first...
35216                 break;
35217             
35218             case 'Grid':
35219                 
35220                 // needs grid and region
35221                 
35222                 //var el = this.getRegion(region).el.createChild();
35223                 /*
35224                  *var el = this.el.createChild();
35225                 // create the grid first...
35226                 cfg.grid.container = el;
35227                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35228                 */
35229                 
35230                 if (region == 'center' && this.active ) {
35231                     cfg.background = false;
35232                 }
35233                 
35234                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35235                 
35236                 this.add(region, ret);
35237                 /*
35238                 if (cfg.background) {
35239                     // render grid on panel activation (if panel background)
35240                     ret.on('activate', function(gp) {
35241                         if (!gp.grid.rendered) {
35242                     //        gp.grid.render(el);
35243                         }
35244                     });
35245                 } else {
35246                   //  cfg.grid.render(el);
35247                 }
35248                 */
35249                 break;
35250            
35251            
35252             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35253                 // it was the old xcomponent building that caused this before.
35254                 // espeically if border is the top element in the tree.
35255                 ret = this;
35256                 break; 
35257                 
35258                     
35259                 
35260                 
35261                 
35262             default:
35263                 /*
35264                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35265                     
35266                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35267                     this.add(region, ret);
35268                 } else {
35269                 */
35270                     Roo.log(cfg);
35271                     throw "Can not add '" + cfg.xtype + "' to Border";
35272                     return null;
35273              
35274                                 
35275              
35276         }
35277         this.beginUpdate();
35278         // add children..
35279         var region = '';
35280         var abn = {};
35281         Roo.each(xitems, function(i)  {
35282             region = nb && i.region ? i.region : false;
35283             
35284             var add = ret.addxtype(i);
35285            
35286             if (region) {
35287                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35288                 if (!i.background) {
35289                     abn[region] = nb[region] ;
35290                 }
35291             }
35292             
35293         });
35294         this.endUpdate();
35295
35296         // make the last non-background panel active..
35297         //if (nb) { Roo.log(abn); }
35298         if (nb) {
35299             
35300             for(var r in abn) {
35301                 region = this.getRegion(r);
35302                 if (region) {
35303                     // tried using nb[r], but it does not work..
35304                      
35305                     region.showPanel(abn[r]);
35306                    
35307                 }
35308             }
35309         }
35310         return ret;
35311         
35312     },
35313     
35314     
35315 // private
35316     factory : function(cfg)
35317     {
35318         
35319         var validRegions = Roo.bootstrap.layout.Border.regions;
35320
35321         var target = cfg.region;
35322         cfg.mgr = this;
35323         
35324         var r = Roo.bootstrap.layout;
35325         Roo.log(target);
35326         switch(target){
35327             case "north":
35328                 return new r.North(cfg);
35329             case "south":
35330                 return new r.South(cfg);
35331             case "east":
35332                 return new r.East(cfg);
35333             case "west":
35334                 return new r.West(cfg);
35335             case "center":
35336                 return new r.Center(cfg);
35337         }
35338         throw 'Layout region "'+target+'" not supported.';
35339     }
35340     
35341     
35342 });
35343  /*
35344  * Based on:
35345  * Ext JS Library 1.1.1
35346  * Copyright(c) 2006-2007, Ext JS, LLC.
35347  *
35348  * Originally Released Under LGPL - original licence link has changed is not relivant.
35349  *
35350  * Fork - LGPL
35351  * <script type="text/javascript">
35352  */
35353  
35354 /**
35355  * @class Roo.bootstrap.layout.Basic
35356  * @extends Roo.util.Observable
35357  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35358  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35359  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35360  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35361  * @cfg {string}   region  the region that it inhabits..
35362  * @cfg {bool}   skipConfig skip config?
35363  * 
35364
35365  */
35366 Roo.bootstrap.layout.Basic = function(config){
35367     
35368     this.mgr = config.mgr;
35369     
35370     this.position = config.region;
35371     
35372     var skipConfig = config.skipConfig;
35373     
35374     this.events = {
35375         /**
35376          * @scope Roo.BasicLayoutRegion
35377          */
35378         
35379         /**
35380          * @event beforeremove
35381          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35382          * @param {Roo.LayoutRegion} this
35383          * @param {Roo.ContentPanel} panel The panel
35384          * @param {Object} e The cancel event object
35385          */
35386         "beforeremove" : true,
35387         /**
35388          * @event invalidated
35389          * Fires when the layout for this region is changed.
35390          * @param {Roo.LayoutRegion} this
35391          */
35392         "invalidated" : true,
35393         /**
35394          * @event visibilitychange
35395          * Fires when this region is shown or hidden 
35396          * @param {Roo.LayoutRegion} this
35397          * @param {Boolean} visibility true or false
35398          */
35399         "visibilitychange" : true,
35400         /**
35401          * @event paneladded
35402          * Fires when a panel is added. 
35403          * @param {Roo.LayoutRegion} this
35404          * @param {Roo.ContentPanel} panel The panel
35405          */
35406         "paneladded" : true,
35407         /**
35408          * @event panelremoved
35409          * Fires when a panel is removed. 
35410          * @param {Roo.LayoutRegion} this
35411          * @param {Roo.ContentPanel} panel The panel
35412          */
35413         "panelremoved" : true,
35414         /**
35415          * @event beforecollapse
35416          * Fires when this region before collapse.
35417          * @param {Roo.LayoutRegion} this
35418          */
35419         "beforecollapse" : true,
35420         /**
35421          * @event collapsed
35422          * Fires when this region is collapsed.
35423          * @param {Roo.LayoutRegion} this
35424          */
35425         "collapsed" : true,
35426         /**
35427          * @event expanded
35428          * Fires when this region is expanded.
35429          * @param {Roo.LayoutRegion} this
35430          */
35431         "expanded" : true,
35432         /**
35433          * @event slideshow
35434          * Fires when this region is slid into view.
35435          * @param {Roo.LayoutRegion} this
35436          */
35437         "slideshow" : true,
35438         /**
35439          * @event slidehide
35440          * Fires when this region slides out of view. 
35441          * @param {Roo.LayoutRegion} this
35442          */
35443         "slidehide" : true,
35444         /**
35445          * @event panelactivated
35446          * Fires when a panel is activated. 
35447          * @param {Roo.LayoutRegion} this
35448          * @param {Roo.ContentPanel} panel The activated panel
35449          */
35450         "panelactivated" : true,
35451         /**
35452          * @event resized
35453          * Fires when the user resizes this region. 
35454          * @param {Roo.LayoutRegion} this
35455          * @param {Number} newSize The new size (width for east/west, height for north/south)
35456          */
35457         "resized" : true
35458     };
35459     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35460     this.panels = new Roo.util.MixedCollection();
35461     this.panels.getKey = this.getPanelId.createDelegate(this);
35462     this.box = null;
35463     this.activePanel = null;
35464     // ensure listeners are added...
35465     
35466     if (config.listeners || config.events) {
35467         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35468             listeners : config.listeners || {},
35469             events : config.events || {}
35470         });
35471     }
35472     
35473     if(skipConfig !== true){
35474         this.applyConfig(config);
35475     }
35476 };
35477
35478 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35479 {
35480     getPanelId : function(p){
35481         return p.getId();
35482     },
35483     
35484     applyConfig : function(config){
35485         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35486         this.config = config;
35487         
35488     },
35489     
35490     /**
35491      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35492      * the width, for horizontal (north, south) the height.
35493      * @param {Number} newSize The new width or height
35494      */
35495     resizeTo : function(newSize){
35496         var el = this.el ? this.el :
35497                  (this.activePanel ? this.activePanel.getEl() : null);
35498         if(el){
35499             switch(this.position){
35500                 case "east":
35501                 case "west":
35502                     el.setWidth(newSize);
35503                     this.fireEvent("resized", this, newSize);
35504                 break;
35505                 case "north":
35506                 case "south":
35507                     el.setHeight(newSize);
35508                     this.fireEvent("resized", this, newSize);
35509                 break;                
35510             }
35511         }
35512     },
35513     
35514     getBox : function(){
35515         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35516     },
35517     
35518     getMargins : function(){
35519         return this.margins;
35520     },
35521     
35522     updateBox : function(box){
35523         this.box = box;
35524         var el = this.activePanel.getEl();
35525         el.dom.style.left = box.x + "px";
35526         el.dom.style.top = box.y + "px";
35527         this.activePanel.setSize(box.width, box.height);
35528     },
35529     
35530     /**
35531      * Returns the container element for this region.
35532      * @return {Roo.Element}
35533      */
35534     getEl : function(){
35535         return this.activePanel;
35536     },
35537     
35538     /**
35539      * Returns true if this region is currently visible.
35540      * @return {Boolean}
35541      */
35542     isVisible : function(){
35543         return this.activePanel ? true : false;
35544     },
35545     
35546     setActivePanel : function(panel){
35547         panel = this.getPanel(panel);
35548         if(this.activePanel && this.activePanel != panel){
35549             this.activePanel.setActiveState(false);
35550             this.activePanel.getEl().setLeftTop(-10000,-10000);
35551         }
35552         this.activePanel = panel;
35553         panel.setActiveState(true);
35554         if(this.box){
35555             panel.setSize(this.box.width, this.box.height);
35556         }
35557         this.fireEvent("panelactivated", this, panel);
35558         this.fireEvent("invalidated");
35559     },
35560     
35561     /**
35562      * Show the specified panel.
35563      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35564      * @return {Roo.ContentPanel} The shown panel or null
35565      */
35566     showPanel : function(panel){
35567         panel = this.getPanel(panel);
35568         if(panel){
35569             this.setActivePanel(panel);
35570         }
35571         return panel;
35572     },
35573     
35574     /**
35575      * Get the active panel for this region.
35576      * @return {Roo.ContentPanel} The active panel or null
35577      */
35578     getActivePanel : function(){
35579         return this.activePanel;
35580     },
35581     
35582     /**
35583      * Add the passed ContentPanel(s)
35584      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35585      * @return {Roo.ContentPanel} The panel added (if only one was added)
35586      */
35587     add : function(panel){
35588         if(arguments.length > 1){
35589             for(var i = 0, len = arguments.length; i < len; i++) {
35590                 this.add(arguments[i]);
35591             }
35592             return null;
35593         }
35594         if(this.hasPanel(panel)){
35595             this.showPanel(panel);
35596             return panel;
35597         }
35598         var el = panel.getEl();
35599         if(el.dom.parentNode != this.mgr.el.dom){
35600             this.mgr.el.dom.appendChild(el.dom);
35601         }
35602         if(panel.setRegion){
35603             panel.setRegion(this);
35604         }
35605         this.panels.add(panel);
35606         el.setStyle("position", "absolute");
35607         if(!panel.background){
35608             this.setActivePanel(panel);
35609             if(this.config.initialSize && this.panels.getCount()==1){
35610                 this.resizeTo(this.config.initialSize);
35611             }
35612         }
35613         this.fireEvent("paneladded", this, panel);
35614         return panel;
35615     },
35616     
35617     /**
35618      * Returns true if the panel is in this region.
35619      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35620      * @return {Boolean}
35621      */
35622     hasPanel : function(panel){
35623         if(typeof panel == "object"){ // must be panel obj
35624             panel = panel.getId();
35625         }
35626         return this.getPanel(panel) ? true : false;
35627     },
35628     
35629     /**
35630      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35631      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35632      * @param {Boolean} preservePanel Overrides the config preservePanel option
35633      * @return {Roo.ContentPanel} The panel that was removed
35634      */
35635     remove : function(panel, preservePanel){
35636         panel = this.getPanel(panel);
35637         if(!panel){
35638             return null;
35639         }
35640         var e = {};
35641         this.fireEvent("beforeremove", this, panel, e);
35642         if(e.cancel === true){
35643             return null;
35644         }
35645         var panelId = panel.getId();
35646         this.panels.removeKey(panelId);
35647         return panel;
35648     },
35649     
35650     /**
35651      * Returns the panel specified or null if it's not in this region.
35652      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35653      * @return {Roo.ContentPanel}
35654      */
35655     getPanel : function(id){
35656         if(typeof id == "object"){ // must be panel obj
35657             return id;
35658         }
35659         return this.panels.get(id);
35660     },
35661     
35662     /**
35663      * Returns this regions position (north/south/east/west/center).
35664      * @return {String} 
35665      */
35666     getPosition: function(){
35667         return this.position;    
35668     }
35669 });/*
35670  * Based on:
35671  * Ext JS Library 1.1.1
35672  * Copyright(c) 2006-2007, Ext JS, LLC.
35673  *
35674  * Originally Released Under LGPL - original licence link has changed is not relivant.
35675  *
35676  * Fork - LGPL
35677  * <script type="text/javascript">
35678  */
35679  
35680 /**
35681  * @class Roo.bootstrap.layout.Region
35682  * @extends Roo.bootstrap.layout.Basic
35683  * This class represents a region in a layout manager.
35684  
35685  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35686  * @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})
35687  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35688  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35689  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35690  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35691  * @cfg {String}    title           The title for the region (overrides panel titles)
35692  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35693  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35694  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35695  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35696  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35697  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35698  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35699  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35700  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35701  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35702
35703  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35704  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35705  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35706  * @cfg {Number}    width           For East/West panels
35707  * @cfg {Number}    height          For North/South panels
35708  * @cfg {Boolean}   split           To show the splitter
35709  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35710  * 
35711  * @cfg {string}   cls             Extra CSS classes to add to region
35712  * 
35713  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35714  * @cfg {string}   region  the region that it inhabits..
35715  *
35716
35717  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35718  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35719
35720  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35721  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35722  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35723  */
35724 Roo.bootstrap.layout.Region = function(config)
35725 {
35726     this.applyConfig(config);
35727
35728     var mgr = config.mgr;
35729     var pos = config.region;
35730     config.skipConfig = true;
35731     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35732     
35733     if (mgr.el) {
35734         this.onRender(mgr.el);   
35735     }
35736      
35737     this.visible = true;
35738     this.collapsed = false;
35739     this.unrendered_panels = [];
35740 };
35741
35742 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35743
35744     position: '', // set by wrapper (eg. north/south etc..)
35745     unrendered_panels : null,  // unrendered panels.
35746     createBody : function(){
35747         /** This region's body element 
35748         * @type Roo.Element */
35749         this.bodyEl = this.el.createChild({
35750                 tag: "div",
35751                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35752         });
35753     },
35754
35755     onRender: function(ctr, pos)
35756     {
35757         var dh = Roo.DomHelper;
35758         /** This region's container element 
35759         * @type Roo.Element */
35760         this.el = dh.append(ctr.dom, {
35761                 tag: "div",
35762                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35763             }, true);
35764         /** This region's title element 
35765         * @type Roo.Element */
35766     
35767         this.titleEl = dh.append(this.el.dom,
35768             {
35769                     tag: "div",
35770                     unselectable: "on",
35771                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35772                     children:[
35773                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35774                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35775                     ]}, true);
35776         
35777         this.titleEl.enableDisplayMode();
35778         /** This region's title text element 
35779         * @type HTMLElement */
35780         this.titleTextEl = this.titleEl.dom.firstChild;
35781         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35782         /*
35783         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35784         this.closeBtn.enableDisplayMode();
35785         this.closeBtn.on("click", this.closeClicked, this);
35786         this.closeBtn.hide();
35787     */
35788         this.createBody(this.config);
35789         if(this.config.hideWhenEmpty){
35790             this.hide();
35791             this.on("paneladded", this.validateVisibility, this);
35792             this.on("panelremoved", this.validateVisibility, this);
35793         }
35794         if(this.autoScroll){
35795             this.bodyEl.setStyle("overflow", "auto");
35796         }else{
35797             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35798         }
35799         //if(c.titlebar !== false){
35800             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35801                 this.titleEl.hide();
35802             }else{
35803                 this.titleEl.show();
35804                 if(this.config.title){
35805                     this.titleTextEl.innerHTML = this.config.title;
35806                 }
35807             }
35808         //}
35809         if(this.config.collapsed){
35810             this.collapse(true);
35811         }
35812         if(this.config.hidden){
35813             this.hide();
35814         }
35815         
35816         if (this.unrendered_panels && this.unrendered_panels.length) {
35817             for (var i =0;i< this.unrendered_panels.length; i++) {
35818                 this.add(this.unrendered_panels[i]);
35819             }
35820             this.unrendered_panels = null;
35821             
35822         }
35823         
35824     },
35825     
35826     applyConfig : function(c)
35827     {
35828         /*
35829          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35830             var dh = Roo.DomHelper;
35831             if(c.titlebar !== false){
35832                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35833                 this.collapseBtn.on("click", this.collapse, this);
35834                 this.collapseBtn.enableDisplayMode();
35835                 /*
35836                 if(c.showPin === true || this.showPin){
35837                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35838                     this.stickBtn.enableDisplayMode();
35839                     this.stickBtn.on("click", this.expand, this);
35840                     this.stickBtn.hide();
35841                 }
35842                 
35843             }
35844             */
35845             /** This region's collapsed element
35846             * @type Roo.Element */
35847             /*
35848              *
35849             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35850                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35851             ]}, true);
35852             
35853             if(c.floatable !== false){
35854                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35855                this.collapsedEl.on("click", this.collapseClick, this);
35856             }
35857
35858             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35859                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35860                    id: "message", unselectable: "on", style:{"float":"left"}});
35861                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35862              }
35863             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35864             this.expandBtn.on("click", this.expand, this);
35865             
35866         }
35867         
35868         if(this.collapseBtn){
35869             this.collapseBtn.setVisible(c.collapsible == true);
35870         }
35871         
35872         this.cmargins = c.cmargins || this.cmargins ||
35873                          (this.position == "west" || this.position == "east" ?
35874                              {top: 0, left: 2, right:2, bottom: 0} :
35875                              {top: 2, left: 0, right:0, bottom: 2});
35876         */
35877         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35878         
35879         
35880         this.bottomTabs = c.tabPosition != "top";
35881         
35882         this.autoScroll = c.autoScroll || false;
35883         
35884         
35885        
35886         
35887         this.duration = c.duration || .30;
35888         this.slideDuration = c.slideDuration || .45;
35889         this.config = c;
35890        
35891     },
35892     /**
35893      * Returns true if this region is currently visible.
35894      * @return {Boolean}
35895      */
35896     isVisible : function(){
35897         return this.visible;
35898     },
35899
35900     /**
35901      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35902      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35903      */
35904     //setCollapsedTitle : function(title){
35905     //    title = title || "&#160;";
35906      //   if(this.collapsedTitleTextEl){
35907       //      this.collapsedTitleTextEl.innerHTML = title;
35908        // }
35909     //},
35910
35911     getBox : function(){
35912         var b;
35913       //  if(!this.collapsed){
35914             b = this.el.getBox(false, true);
35915        // }else{
35916           //  b = this.collapsedEl.getBox(false, true);
35917         //}
35918         return b;
35919     },
35920
35921     getMargins : function(){
35922         return this.margins;
35923         //return this.collapsed ? this.cmargins : this.margins;
35924     },
35925 /*
35926     highlight : function(){
35927         this.el.addClass("x-layout-panel-dragover");
35928     },
35929
35930     unhighlight : function(){
35931         this.el.removeClass("x-layout-panel-dragover");
35932     },
35933 */
35934     updateBox : function(box)
35935     {
35936         if (!this.bodyEl) {
35937             return; // not rendered yet..
35938         }
35939         
35940         this.box = box;
35941         if(!this.collapsed){
35942             this.el.dom.style.left = box.x + "px";
35943             this.el.dom.style.top = box.y + "px";
35944             this.updateBody(box.width, box.height);
35945         }else{
35946             this.collapsedEl.dom.style.left = box.x + "px";
35947             this.collapsedEl.dom.style.top = box.y + "px";
35948             this.collapsedEl.setSize(box.width, box.height);
35949         }
35950         if(this.tabs){
35951             this.tabs.autoSizeTabs();
35952         }
35953     },
35954
35955     updateBody : function(w, h)
35956     {
35957         if(w !== null){
35958             this.el.setWidth(w);
35959             w -= this.el.getBorderWidth("rl");
35960             if(this.config.adjustments){
35961                 w += this.config.adjustments[0];
35962             }
35963         }
35964         if(h !== null && h > 0){
35965             this.el.setHeight(h);
35966             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35967             h -= this.el.getBorderWidth("tb");
35968             if(this.config.adjustments){
35969                 h += this.config.adjustments[1];
35970             }
35971             this.bodyEl.setHeight(h);
35972             if(this.tabs){
35973                 h = this.tabs.syncHeight(h);
35974             }
35975         }
35976         if(this.panelSize){
35977             w = w !== null ? w : this.panelSize.width;
35978             h = h !== null ? h : this.panelSize.height;
35979         }
35980         if(this.activePanel){
35981             var el = this.activePanel.getEl();
35982             w = w !== null ? w : el.getWidth();
35983             h = h !== null ? h : el.getHeight();
35984             this.panelSize = {width: w, height: h};
35985             this.activePanel.setSize(w, h);
35986         }
35987         if(Roo.isIE && this.tabs){
35988             this.tabs.el.repaint();
35989         }
35990     },
35991
35992     /**
35993      * Returns the container element for this region.
35994      * @return {Roo.Element}
35995      */
35996     getEl : function(){
35997         return this.el;
35998     },
35999
36000     /**
36001      * Hides this region.
36002      */
36003     hide : function(){
36004         //if(!this.collapsed){
36005             this.el.dom.style.left = "-2000px";
36006             this.el.hide();
36007         //}else{
36008          //   this.collapsedEl.dom.style.left = "-2000px";
36009          //   this.collapsedEl.hide();
36010        // }
36011         this.visible = false;
36012         this.fireEvent("visibilitychange", this, false);
36013     },
36014
36015     /**
36016      * Shows this region if it was previously hidden.
36017      */
36018     show : function(){
36019         //if(!this.collapsed){
36020             this.el.show();
36021         //}else{
36022         //    this.collapsedEl.show();
36023        // }
36024         this.visible = true;
36025         this.fireEvent("visibilitychange", this, true);
36026     },
36027 /*
36028     closeClicked : function(){
36029         if(this.activePanel){
36030             this.remove(this.activePanel);
36031         }
36032     },
36033
36034     collapseClick : function(e){
36035         if(this.isSlid){
36036            e.stopPropagation();
36037            this.slideIn();
36038         }else{
36039            e.stopPropagation();
36040            this.slideOut();
36041         }
36042     },
36043 */
36044     /**
36045      * Collapses this region.
36046      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36047      */
36048     /*
36049     collapse : function(skipAnim, skipCheck = false){
36050         if(this.collapsed) {
36051             return;
36052         }
36053         
36054         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36055             
36056             this.collapsed = true;
36057             if(this.split){
36058                 this.split.el.hide();
36059             }
36060             if(this.config.animate && skipAnim !== true){
36061                 this.fireEvent("invalidated", this);
36062                 this.animateCollapse();
36063             }else{
36064                 this.el.setLocation(-20000,-20000);
36065                 this.el.hide();
36066                 this.collapsedEl.show();
36067                 this.fireEvent("collapsed", this);
36068                 this.fireEvent("invalidated", this);
36069             }
36070         }
36071         
36072     },
36073 */
36074     animateCollapse : function(){
36075         // overridden
36076     },
36077
36078     /**
36079      * Expands this region if it was previously collapsed.
36080      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36081      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36082      */
36083     /*
36084     expand : function(e, skipAnim){
36085         if(e) {
36086             e.stopPropagation();
36087         }
36088         if(!this.collapsed || this.el.hasActiveFx()) {
36089             return;
36090         }
36091         if(this.isSlid){
36092             this.afterSlideIn();
36093             skipAnim = true;
36094         }
36095         this.collapsed = false;
36096         if(this.config.animate && skipAnim !== true){
36097             this.animateExpand();
36098         }else{
36099             this.el.show();
36100             if(this.split){
36101                 this.split.el.show();
36102             }
36103             this.collapsedEl.setLocation(-2000,-2000);
36104             this.collapsedEl.hide();
36105             this.fireEvent("invalidated", this);
36106             this.fireEvent("expanded", this);
36107         }
36108     },
36109 */
36110     animateExpand : function(){
36111         // overridden
36112     },
36113
36114     initTabs : function()
36115     {
36116         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36117         
36118         var ts = new Roo.bootstrap.panel.Tabs({
36119                 el: this.bodyEl.dom,
36120                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36121                 disableTooltips: this.config.disableTabTips,
36122                 toolbar : this.config.toolbar
36123             });
36124         
36125         if(this.config.hideTabs){
36126             ts.stripWrap.setDisplayed(false);
36127         }
36128         this.tabs = ts;
36129         ts.resizeTabs = this.config.resizeTabs === true;
36130         ts.minTabWidth = this.config.minTabWidth || 40;
36131         ts.maxTabWidth = this.config.maxTabWidth || 250;
36132         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36133         ts.monitorResize = false;
36134         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36135         ts.bodyEl.addClass('roo-layout-tabs-body');
36136         this.panels.each(this.initPanelAsTab, this);
36137     },
36138
36139     initPanelAsTab : function(panel){
36140         var ti = this.tabs.addTab(
36141             panel.getEl().id,
36142             panel.getTitle(),
36143             null,
36144             this.config.closeOnTab && panel.isClosable(),
36145             panel.tpl
36146         );
36147         if(panel.tabTip !== undefined){
36148             ti.setTooltip(panel.tabTip);
36149         }
36150         ti.on("activate", function(){
36151               this.setActivePanel(panel);
36152         }, this);
36153         
36154         if(this.config.closeOnTab){
36155             ti.on("beforeclose", function(t, e){
36156                 e.cancel = true;
36157                 this.remove(panel);
36158             }, this);
36159         }
36160         
36161         panel.tabItem = ti;
36162         
36163         return ti;
36164     },
36165
36166     updatePanelTitle : function(panel, title)
36167     {
36168         if(this.activePanel == panel){
36169             this.updateTitle(title);
36170         }
36171         if(this.tabs){
36172             var ti = this.tabs.getTab(panel.getEl().id);
36173             ti.setText(title);
36174             if(panel.tabTip !== undefined){
36175                 ti.setTooltip(panel.tabTip);
36176             }
36177         }
36178     },
36179
36180     updateTitle : function(title){
36181         if(this.titleTextEl && !this.config.title){
36182             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36183         }
36184     },
36185
36186     setActivePanel : function(panel)
36187     {
36188         panel = this.getPanel(panel);
36189         if(this.activePanel && this.activePanel != panel){
36190             if(this.activePanel.setActiveState(false) === false){
36191                 return;
36192             }
36193         }
36194         this.activePanel = panel;
36195         panel.setActiveState(true);
36196         if(this.panelSize){
36197             panel.setSize(this.panelSize.width, this.panelSize.height);
36198         }
36199         if(this.closeBtn){
36200             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36201         }
36202         this.updateTitle(panel.getTitle());
36203         if(this.tabs){
36204             this.fireEvent("invalidated", this);
36205         }
36206         this.fireEvent("panelactivated", this, panel);
36207     },
36208
36209     /**
36210      * Shows the specified panel.
36211      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36212      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36213      */
36214     showPanel : function(panel)
36215     {
36216         panel = this.getPanel(panel);
36217         if(panel){
36218             if(this.tabs){
36219                 var tab = this.tabs.getTab(panel.getEl().id);
36220                 if(tab.isHidden()){
36221                     this.tabs.unhideTab(tab.id);
36222                 }
36223                 tab.activate();
36224             }else{
36225                 this.setActivePanel(panel);
36226             }
36227         }
36228         return panel;
36229     },
36230
36231     /**
36232      * Get the active panel for this region.
36233      * @return {Roo.ContentPanel} The active panel or null
36234      */
36235     getActivePanel : function(){
36236         return this.activePanel;
36237     },
36238
36239     validateVisibility : function(){
36240         if(this.panels.getCount() < 1){
36241             this.updateTitle("&#160;");
36242             this.closeBtn.hide();
36243             this.hide();
36244         }else{
36245             if(!this.isVisible()){
36246                 this.show();
36247             }
36248         }
36249     },
36250
36251     /**
36252      * Adds the passed ContentPanel(s) to this region.
36253      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36254      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36255      */
36256     add : function(panel)
36257     {
36258         if(arguments.length > 1){
36259             for(var i = 0, len = arguments.length; i < len; i++) {
36260                 this.add(arguments[i]);
36261             }
36262             return null;
36263         }
36264         
36265         // if we have not been rendered yet, then we can not really do much of this..
36266         if (!this.bodyEl) {
36267             this.unrendered_panels.push(panel);
36268             return panel;
36269         }
36270         
36271         
36272         
36273         
36274         if(this.hasPanel(panel)){
36275             this.showPanel(panel);
36276             return panel;
36277         }
36278         panel.setRegion(this);
36279         this.panels.add(panel);
36280        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36281             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36282             // and hide them... ???
36283             this.bodyEl.dom.appendChild(panel.getEl().dom);
36284             if(panel.background !== true){
36285                 this.setActivePanel(panel);
36286             }
36287             this.fireEvent("paneladded", this, panel);
36288             return panel;
36289         }
36290         */
36291         if(!this.tabs){
36292             this.initTabs();
36293         }else{
36294             this.initPanelAsTab(panel);
36295         }
36296         
36297         
36298         if(panel.background !== true){
36299             this.tabs.activate(panel.getEl().id);
36300         }
36301         this.fireEvent("paneladded", this, panel);
36302         return panel;
36303     },
36304
36305     /**
36306      * Hides the tab for the specified panel.
36307      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36308      */
36309     hidePanel : function(panel){
36310         if(this.tabs && (panel = this.getPanel(panel))){
36311             this.tabs.hideTab(panel.getEl().id);
36312         }
36313     },
36314
36315     /**
36316      * Unhides the tab for a previously hidden panel.
36317      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36318      */
36319     unhidePanel : function(panel){
36320         if(this.tabs && (panel = this.getPanel(panel))){
36321             this.tabs.unhideTab(panel.getEl().id);
36322         }
36323     },
36324
36325     clearPanels : function(){
36326         while(this.panels.getCount() > 0){
36327              this.remove(this.panels.first());
36328         }
36329     },
36330
36331     /**
36332      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36333      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36334      * @param {Boolean} preservePanel Overrides the config preservePanel option
36335      * @return {Roo.ContentPanel} The panel that was removed
36336      */
36337     remove : function(panel, preservePanel)
36338     {
36339         panel = this.getPanel(panel);
36340         if(!panel){
36341             return null;
36342         }
36343         var e = {};
36344         this.fireEvent("beforeremove", this, panel, e);
36345         if(e.cancel === true){
36346             return null;
36347         }
36348         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36349         var panelId = panel.getId();
36350         this.panels.removeKey(panelId);
36351         if(preservePanel){
36352             document.body.appendChild(panel.getEl().dom);
36353         }
36354         if(this.tabs){
36355             this.tabs.removeTab(panel.getEl().id);
36356         }else if (!preservePanel){
36357             this.bodyEl.dom.removeChild(panel.getEl().dom);
36358         }
36359         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36360             var p = this.panels.first();
36361             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36362             tempEl.appendChild(p.getEl().dom);
36363             this.bodyEl.update("");
36364             this.bodyEl.dom.appendChild(p.getEl().dom);
36365             tempEl = null;
36366             this.updateTitle(p.getTitle());
36367             this.tabs = null;
36368             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36369             this.setActivePanel(p);
36370         }
36371         panel.setRegion(null);
36372         if(this.activePanel == panel){
36373             this.activePanel = null;
36374         }
36375         if(this.config.autoDestroy !== false && preservePanel !== true){
36376             try{panel.destroy();}catch(e){}
36377         }
36378         this.fireEvent("panelremoved", this, panel);
36379         return panel;
36380     },
36381
36382     /**
36383      * Returns the TabPanel component used by this region
36384      * @return {Roo.TabPanel}
36385      */
36386     getTabs : function(){
36387         return this.tabs;
36388     },
36389
36390     createTool : function(parentEl, className){
36391         var btn = Roo.DomHelper.append(parentEl, {
36392             tag: "div",
36393             cls: "x-layout-tools-button",
36394             children: [ {
36395                 tag: "div",
36396                 cls: "roo-layout-tools-button-inner " + className,
36397                 html: "&#160;"
36398             }]
36399         }, true);
36400         btn.addClassOnOver("roo-layout-tools-button-over");
36401         return btn;
36402     }
36403 });/*
36404  * Based on:
36405  * Ext JS Library 1.1.1
36406  * Copyright(c) 2006-2007, Ext JS, LLC.
36407  *
36408  * Originally Released Under LGPL - original licence link has changed is not relivant.
36409  *
36410  * Fork - LGPL
36411  * <script type="text/javascript">
36412  */
36413  
36414
36415
36416 /**
36417  * @class Roo.SplitLayoutRegion
36418  * @extends Roo.LayoutRegion
36419  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36420  */
36421 Roo.bootstrap.layout.Split = function(config){
36422     this.cursor = config.cursor;
36423     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36424 };
36425
36426 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36427 {
36428     splitTip : "Drag to resize.",
36429     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36430     useSplitTips : false,
36431
36432     applyConfig : function(config){
36433         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36434     },
36435     
36436     onRender : function(ctr,pos) {
36437         
36438         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36439         if(!this.config.split){
36440             return;
36441         }
36442         if(!this.split){
36443             
36444             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36445                             tag: "div",
36446                             id: this.el.id + "-split",
36447                             cls: "roo-layout-split roo-layout-split-"+this.position,
36448                             html: "&#160;"
36449             });
36450             /** The SplitBar for this region 
36451             * @type Roo.SplitBar */
36452             // does not exist yet...
36453             Roo.log([this.position, this.orientation]);
36454             
36455             this.split = new Roo.bootstrap.SplitBar({
36456                 dragElement : splitEl,
36457                 resizingElement: this.el,
36458                 orientation : this.orientation
36459             });
36460             
36461             this.split.on("moved", this.onSplitMove, this);
36462             this.split.useShim = this.config.useShim === true;
36463             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36464             if(this.useSplitTips){
36465                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36466             }
36467             //if(config.collapsible){
36468             //    this.split.el.on("dblclick", this.collapse,  this);
36469             //}
36470         }
36471         if(typeof this.config.minSize != "undefined"){
36472             this.split.minSize = this.config.minSize;
36473         }
36474         if(typeof this.config.maxSize != "undefined"){
36475             this.split.maxSize = this.config.maxSize;
36476         }
36477         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36478             this.hideSplitter();
36479         }
36480         
36481     },
36482
36483     getHMaxSize : function(){
36484          var cmax = this.config.maxSize || 10000;
36485          var center = this.mgr.getRegion("center");
36486          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36487     },
36488
36489     getVMaxSize : function(){
36490          var cmax = this.config.maxSize || 10000;
36491          var center = this.mgr.getRegion("center");
36492          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36493     },
36494
36495     onSplitMove : function(split, newSize){
36496         this.fireEvent("resized", this, newSize);
36497     },
36498     
36499     /** 
36500      * Returns the {@link Roo.SplitBar} for this region.
36501      * @return {Roo.SplitBar}
36502      */
36503     getSplitBar : function(){
36504         return this.split;
36505     },
36506     
36507     hide : function(){
36508         this.hideSplitter();
36509         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36510     },
36511
36512     hideSplitter : function(){
36513         if(this.split){
36514             this.split.el.setLocation(-2000,-2000);
36515             this.split.el.hide();
36516         }
36517     },
36518
36519     show : function(){
36520         if(this.split){
36521             this.split.el.show();
36522         }
36523         Roo.bootstrap.layout.Split.superclass.show.call(this);
36524     },
36525     
36526     beforeSlide: function(){
36527         if(Roo.isGecko){// firefox overflow auto bug workaround
36528             this.bodyEl.clip();
36529             if(this.tabs) {
36530                 this.tabs.bodyEl.clip();
36531             }
36532             if(this.activePanel){
36533                 this.activePanel.getEl().clip();
36534                 
36535                 if(this.activePanel.beforeSlide){
36536                     this.activePanel.beforeSlide();
36537                 }
36538             }
36539         }
36540     },
36541     
36542     afterSlide : function(){
36543         if(Roo.isGecko){// firefox overflow auto bug workaround
36544             this.bodyEl.unclip();
36545             if(this.tabs) {
36546                 this.tabs.bodyEl.unclip();
36547             }
36548             if(this.activePanel){
36549                 this.activePanel.getEl().unclip();
36550                 if(this.activePanel.afterSlide){
36551                     this.activePanel.afterSlide();
36552                 }
36553             }
36554         }
36555     },
36556
36557     initAutoHide : function(){
36558         if(this.autoHide !== false){
36559             if(!this.autoHideHd){
36560                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36561                 this.autoHideHd = {
36562                     "mouseout": function(e){
36563                         if(!e.within(this.el, true)){
36564                             st.delay(500);
36565                         }
36566                     },
36567                     "mouseover" : function(e){
36568                         st.cancel();
36569                     },
36570                     scope : this
36571                 };
36572             }
36573             this.el.on(this.autoHideHd);
36574         }
36575     },
36576
36577     clearAutoHide : function(){
36578         if(this.autoHide !== false){
36579             this.el.un("mouseout", this.autoHideHd.mouseout);
36580             this.el.un("mouseover", this.autoHideHd.mouseover);
36581         }
36582     },
36583
36584     clearMonitor : function(){
36585         Roo.get(document).un("click", this.slideInIf, this);
36586     },
36587
36588     // these names are backwards but not changed for compat
36589     slideOut : function(){
36590         if(this.isSlid || this.el.hasActiveFx()){
36591             return;
36592         }
36593         this.isSlid = true;
36594         if(this.collapseBtn){
36595             this.collapseBtn.hide();
36596         }
36597         this.closeBtnState = this.closeBtn.getStyle('display');
36598         this.closeBtn.hide();
36599         if(this.stickBtn){
36600             this.stickBtn.show();
36601         }
36602         this.el.show();
36603         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36604         this.beforeSlide();
36605         this.el.setStyle("z-index", 10001);
36606         this.el.slideIn(this.getSlideAnchor(), {
36607             callback: function(){
36608                 this.afterSlide();
36609                 this.initAutoHide();
36610                 Roo.get(document).on("click", this.slideInIf, this);
36611                 this.fireEvent("slideshow", this);
36612             },
36613             scope: this,
36614             block: true
36615         });
36616     },
36617
36618     afterSlideIn : function(){
36619         this.clearAutoHide();
36620         this.isSlid = false;
36621         this.clearMonitor();
36622         this.el.setStyle("z-index", "");
36623         if(this.collapseBtn){
36624             this.collapseBtn.show();
36625         }
36626         this.closeBtn.setStyle('display', this.closeBtnState);
36627         if(this.stickBtn){
36628             this.stickBtn.hide();
36629         }
36630         this.fireEvent("slidehide", this);
36631     },
36632
36633     slideIn : function(cb){
36634         if(!this.isSlid || this.el.hasActiveFx()){
36635             Roo.callback(cb);
36636             return;
36637         }
36638         this.isSlid = false;
36639         this.beforeSlide();
36640         this.el.slideOut(this.getSlideAnchor(), {
36641             callback: function(){
36642                 this.el.setLeftTop(-10000, -10000);
36643                 this.afterSlide();
36644                 this.afterSlideIn();
36645                 Roo.callback(cb);
36646             },
36647             scope: this,
36648             block: true
36649         });
36650     },
36651     
36652     slideInIf : function(e){
36653         if(!e.within(this.el)){
36654             this.slideIn();
36655         }
36656     },
36657
36658     animateCollapse : function(){
36659         this.beforeSlide();
36660         this.el.setStyle("z-index", 20000);
36661         var anchor = this.getSlideAnchor();
36662         this.el.slideOut(anchor, {
36663             callback : function(){
36664                 this.el.setStyle("z-index", "");
36665                 this.collapsedEl.slideIn(anchor, {duration:.3});
36666                 this.afterSlide();
36667                 this.el.setLocation(-10000,-10000);
36668                 this.el.hide();
36669                 this.fireEvent("collapsed", this);
36670             },
36671             scope: this,
36672             block: true
36673         });
36674     },
36675
36676     animateExpand : function(){
36677         this.beforeSlide();
36678         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36679         this.el.setStyle("z-index", 20000);
36680         this.collapsedEl.hide({
36681             duration:.1
36682         });
36683         this.el.slideIn(this.getSlideAnchor(), {
36684             callback : function(){
36685                 this.el.setStyle("z-index", "");
36686                 this.afterSlide();
36687                 if(this.split){
36688                     this.split.el.show();
36689                 }
36690                 this.fireEvent("invalidated", this);
36691                 this.fireEvent("expanded", this);
36692             },
36693             scope: this,
36694             block: true
36695         });
36696     },
36697
36698     anchors : {
36699         "west" : "left",
36700         "east" : "right",
36701         "north" : "top",
36702         "south" : "bottom"
36703     },
36704
36705     sanchors : {
36706         "west" : "l",
36707         "east" : "r",
36708         "north" : "t",
36709         "south" : "b"
36710     },
36711
36712     canchors : {
36713         "west" : "tl-tr",
36714         "east" : "tr-tl",
36715         "north" : "tl-bl",
36716         "south" : "bl-tl"
36717     },
36718
36719     getAnchor : function(){
36720         return this.anchors[this.position];
36721     },
36722
36723     getCollapseAnchor : function(){
36724         return this.canchors[this.position];
36725     },
36726
36727     getSlideAnchor : function(){
36728         return this.sanchors[this.position];
36729     },
36730
36731     getAlignAdj : function(){
36732         var cm = this.cmargins;
36733         switch(this.position){
36734             case "west":
36735                 return [0, 0];
36736             break;
36737             case "east":
36738                 return [0, 0];
36739             break;
36740             case "north":
36741                 return [0, 0];
36742             break;
36743             case "south":
36744                 return [0, 0];
36745             break;
36746         }
36747     },
36748
36749     getExpandAdj : function(){
36750         var c = this.collapsedEl, cm = this.cmargins;
36751         switch(this.position){
36752             case "west":
36753                 return [-(cm.right+c.getWidth()+cm.left), 0];
36754             break;
36755             case "east":
36756                 return [cm.right+c.getWidth()+cm.left, 0];
36757             break;
36758             case "north":
36759                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36760             break;
36761             case "south":
36762                 return [0, cm.top+cm.bottom+c.getHeight()];
36763             break;
36764         }
36765     }
36766 });/*
36767  * Based on:
36768  * Ext JS Library 1.1.1
36769  * Copyright(c) 2006-2007, Ext JS, LLC.
36770  *
36771  * Originally Released Under LGPL - original licence link has changed is not relivant.
36772  *
36773  * Fork - LGPL
36774  * <script type="text/javascript">
36775  */
36776 /*
36777  * These classes are private internal classes
36778  */
36779 Roo.bootstrap.layout.Center = function(config){
36780     config.region = "center";
36781     Roo.bootstrap.layout.Region.call(this, config);
36782     this.visible = true;
36783     this.minWidth = config.minWidth || 20;
36784     this.minHeight = config.minHeight || 20;
36785 };
36786
36787 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36788     hide : function(){
36789         // center panel can't be hidden
36790     },
36791     
36792     show : function(){
36793         // center panel can't be hidden
36794     },
36795     
36796     getMinWidth: function(){
36797         return this.minWidth;
36798     },
36799     
36800     getMinHeight: function(){
36801         return this.minHeight;
36802     }
36803 });
36804
36805
36806
36807
36808  
36809
36810
36811
36812
36813
36814 Roo.bootstrap.layout.North = function(config)
36815 {
36816     config.region = 'north';
36817     config.cursor = 'n-resize';
36818     
36819     Roo.bootstrap.layout.Split.call(this, config);
36820     
36821     
36822     if(this.split){
36823         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36824         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36825         this.split.el.addClass("roo-layout-split-v");
36826     }
36827     var size = config.initialSize || config.height;
36828     if(typeof size != "undefined"){
36829         this.el.setHeight(size);
36830     }
36831 };
36832 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36833 {
36834     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36835     
36836     
36837     
36838     getBox : function(){
36839         if(this.collapsed){
36840             return this.collapsedEl.getBox();
36841         }
36842         var box = this.el.getBox();
36843         if(this.split){
36844             box.height += this.split.el.getHeight();
36845         }
36846         return box;
36847     },
36848     
36849     updateBox : function(box){
36850         if(this.split && !this.collapsed){
36851             box.height -= this.split.el.getHeight();
36852             this.split.el.setLeft(box.x);
36853             this.split.el.setTop(box.y+box.height);
36854             this.split.el.setWidth(box.width);
36855         }
36856         if(this.collapsed){
36857             this.updateBody(box.width, null);
36858         }
36859         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36860     }
36861 });
36862
36863
36864
36865
36866
36867 Roo.bootstrap.layout.South = function(config){
36868     config.region = 'south';
36869     config.cursor = 's-resize';
36870     Roo.bootstrap.layout.Split.call(this, config);
36871     if(this.split){
36872         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36873         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36874         this.split.el.addClass("roo-layout-split-v");
36875     }
36876     var size = config.initialSize || config.height;
36877     if(typeof size != "undefined"){
36878         this.el.setHeight(size);
36879     }
36880 };
36881
36882 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36883     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36884     getBox : function(){
36885         if(this.collapsed){
36886             return this.collapsedEl.getBox();
36887         }
36888         var box = this.el.getBox();
36889         if(this.split){
36890             var sh = this.split.el.getHeight();
36891             box.height += sh;
36892             box.y -= sh;
36893         }
36894         return box;
36895     },
36896     
36897     updateBox : function(box){
36898         if(this.split && !this.collapsed){
36899             var sh = this.split.el.getHeight();
36900             box.height -= sh;
36901             box.y += sh;
36902             this.split.el.setLeft(box.x);
36903             this.split.el.setTop(box.y-sh);
36904             this.split.el.setWidth(box.width);
36905         }
36906         if(this.collapsed){
36907             this.updateBody(box.width, null);
36908         }
36909         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36910     }
36911 });
36912
36913 Roo.bootstrap.layout.East = function(config){
36914     config.region = "east";
36915     config.cursor = "e-resize";
36916     Roo.bootstrap.layout.Split.call(this, config);
36917     if(this.split){
36918         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36919         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36920         this.split.el.addClass("roo-layout-split-h");
36921     }
36922     var size = config.initialSize || config.width;
36923     if(typeof size != "undefined"){
36924         this.el.setWidth(size);
36925     }
36926 };
36927 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36928     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36929     getBox : function(){
36930         if(this.collapsed){
36931             return this.collapsedEl.getBox();
36932         }
36933         var box = this.el.getBox();
36934         if(this.split){
36935             var sw = this.split.el.getWidth();
36936             box.width += sw;
36937             box.x -= sw;
36938         }
36939         return box;
36940     },
36941
36942     updateBox : function(box){
36943         if(this.split && !this.collapsed){
36944             var sw = this.split.el.getWidth();
36945             box.width -= sw;
36946             this.split.el.setLeft(box.x);
36947             this.split.el.setTop(box.y);
36948             this.split.el.setHeight(box.height);
36949             box.x += sw;
36950         }
36951         if(this.collapsed){
36952             this.updateBody(null, box.height);
36953         }
36954         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36955     }
36956 });
36957
36958 Roo.bootstrap.layout.West = function(config){
36959     config.region = "west";
36960     config.cursor = "w-resize";
36961     
36962     Roo.bootstrap.layout.Split.call(this, config);
36963     if(this.split){
36964         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36965         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36966         this.split.el.addClass("roo-layout-split-h");
36967     }
36968     
36969 };
36970 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36971     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36972     
36973     onRender: function(ctr, pos)
36974     {
36975         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36976         var size = this.config.initialSize || this.config.width;
36977         if(typeof size != "undefined"){
36978             this.el.setWidth(size);
36979         }
36980     },
36981     
36982     getBox : function(){
36983         if(this.collapsed){
36984             return this.collapsedEl.getBox();
36985         }
36986         var box = this.el.getBox();
36987         if(this.split){
36988             box.width += this.split.el.getWidth();
36989         }
36990         return box;
36991     },
36992     
36993     updateBox : function(box){
36994         if(this.split && !this.collapsed){
36995             var sw = this.split.el.getWidth();
36996             box.width -= sw;
36997             this.split.el.setLeft(box.x+box.width);
36998             this.split.el.setTop(box.y);
36999             this.split.el.setHeight(box.height);
37000         }
37001         if(this.collapsed){
37002             this.updateBody(null, box.height);
37003         }
37004         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37005     }
37006 });
37007 Roo.namespace("Roo.bootstrap.panel");/*
37008  * Based on:
37009  * Ext JS Library 1.1.1
37010  * Copyright(c) 2006-2007, Ext JS, LLC.
37011  *
37012  * Originally Released Under LGPL - original licence link has changed is not relivant.
37013  *
37014  * Fork - LGPL
37015  * <script type="text/javascript">
37016  */
37017 /**
37018  * @class Roo.ContentPanel
37019  * @extends Roo.util.Observable
37020  * A basic ContentPanel element.
37021  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37022  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37023  * @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
37024  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37025  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37026  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37027  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37028  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37029  * @cfg {String} title          The title for this panel
37030  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37031  * @cfg {String} url            Calls {@link #setUrl} with this value
37032  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37033  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37034  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37035  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37036  * @cfg {Boolean} badges render the badges
37037
37038  * @constructor
37039  * Create a new ContentPanel.
37040  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37041  * @param {String/Object} config A string to set only the title or a config object
37042  * @param {String} content (optional) Set the HTML content for this panel
37043  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37044  */
37045 Roo.bootstrap.panel.Content = function( config){
37046     
37047     this.tpl = config.tpl || false;
37048     
37049     var el = config.el;
37050     var content = config.content;
37051
37052     if(config.autoCreate){ // xtype is available if this is called from factory
37053         el = Roo.id();
37054     }
37055     this.el = Roo.get(el);
37056     if(!this.el && config && config.autoCreate){
37057         if(typeof config.autoCreate == "object"){
37058             if(!config.autoCreate.id){
37059                 config.autoCreate.id = config.id||el;
37060             }
37061             this.el = Roo.DomHelper.append(document.body,
37062                         config.autoCreate, true);
37063         }else{
37064             var elcfg =  {   tag: "div",
37065                             cls: "roo-layout-inactive-content",
37066                             id: config.id||el
37067                             };
37068             if (config.html) {
37069                 elcfg.html = config.html;
37070                 
37071             }
37072                         
37073             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37074         }
37075     } 
37076     this.closable = false;
37077     this.loaded = false;
37078     this.active = false;
37079    
37080       
37081     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37082         
37083         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37084         
37085         this.wrapEl = this.el; //this.el.wrap();
37086         var ti = [];
37087         if (config.toolbar.items) {
37088             ti = config.toolbar.items ;
37089             delete config.toolbar.items ;
37090         }
37091         
37092         var nitems = [];
37093         this.toolbar.render(this.wrapEl, 'before');
37094         for(var i =0;i < ti.length;i++) {
37095           //  Roo.log(['add child', items[i]]);
37096             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37097         }
37098         this.toolbar.items = nitems;
37099         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37100         delete config.toolbar;
37101         
37102     }
37103     /*
37104     // xtype created footer. - not sure if will work as we normally have to render first..
37105     if (this.footer && !this.footer.el && this.footer.xtype) {
37106         if (!this.wrapEl) {
37107             this.wrapEl = this.el.wrap();
37108         }
37109     
37110         this.footer.container = this.wrapEl.createChild();
37111          
37112         this.footer = Roo.factory(this.footer, Roo);
37113         
37114     }
37115     */
37116     
37117      if(typeof config == "string"){
37118         this.title = config;
37119     }else{
37120         Roo.apply(this, config);
37121     }
37122     
37123     if(this.resizeEl){
37124         this.resizeEl = Roo.get(this.resizeEl, true);
37125     }else{
37126         this.resizeEl = this.el;
37127     }
37128     // handle view.xtype
37129     
37130  
37131     
37132     
37133     this.addEvents({
37134         /**
37135          * @event activate
37136          * Fires when this panel is activated. 
37137          * @param {Roo.ContentPanel} this
37138          */
37139         "activate" : true,
37140         /**
37141          * @event deactivate
37142          * Fires when this panel is activated. 
37143          * @param {Roo.ContentPanel} this
37144          */
37145         "deactivate" : true,
37146
37147         /**
37148          * @event resize
37149          * Fires when this panel is resized if fitToFrame is true.
37150          * @param {Roo.ContentPanel} this
37151          * @param {Number} width The width after any component adjustments
37152          * @param {Number} height The height after any component adjustments
37153          */
37154         "resize" : true,
37155         
37156          /**
37157          * @event render
37158          * Fires when this tab is created
37159          * @param {Roo.ContentPanel} this
37160          */
37161         "render" : true
37162         
37163         
37164         
37165     });
37166     
37167
37168     
37169     
37170     if(this.autoScroll){
37171         this.resizeEl.setStyle("overflow", "auto");
37172     } else {
37173         // fix randome scrolling
37174         //this.el.on('scroll', function() {
37175         //    Roo.log('fix random scolling');
37176         //    this.scrollTo('top',0); 
37177         //});
37178     }
37179     content = content || this.content;
37180     if(content){
37181         this.setContent(content);
37182     }
37183     if(config && config.url){
37184         this.setUrl(this.url, this.params, this.loadOnce);
37185     }
37186     
37187     
37188     
37189     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37190     
37191     if (this.view && typeof(this.view.xtype) != 'undefined') {
37192         this.view.el = this.el.appendChild(document.createElement("div"));
37193         this.view = Roo.factory(this.view); 
37194         this.view.render  &&  this.view.render(false, '');  
37195     }
37196     
37197     
37198     this.fireEvent('render', this);
37199 };
37200
37201 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37202     
37203     tabTip : '',
37204     
37205     setRegion : function(region){
37206         this.region = region;
37207         this.setActiveClass(region && !this.background);
37208     },
37209     
37210     
37211     setActiveClass: function(state)
37212     {
37213         if(state){
37214            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37215            this.el.setStyle('position','relative');
37216         }else{
37217            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37218            this.el.setStyle('position', 'absolute');
37219         } 
37220     },
37221     
37222     /**
37223      * Returns the toolbar for this Panel if one was configured. 
37224      * @return {Roo.Toolbar} 
37225      */
37226     getToolbar : function(){
37227         return this.toolbar;
37228     },
37229     
37230     setActiveState : function(active)
37231     {
37232         this.active = active;
37233         this.setActiveClass(active);
37234         if(!active){
37235             if(this.fireEvent("deactivate", this) === false){
37236                 return false;
37237             }
37238             return true;
37239         }
37240         this.fireEvent("activate", this);
37241         return true;
37242     },
37243     /**
37244      * Updates this panel's element
37245      * @param {String} content The new content
37246      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37247     */
37248     setContent : function(content, loadScripts){
37249         this.el.update(content, loadScripts);
37250     },
37251
37252     ignoreResize : function(w, h){
37253         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37254             return true;
37255         }else{
37256             this.lastSize = {width: w, height: h};
37257             return false;
37258         }
37259     },
37260     /**
37261      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37262      * @return {Roo.UpdateManager} The UpdateManager
37263      */
37264     getUpdateManager : function(){
37265         return this.el.getUpdateManager();
37266     },
37267      /**
37268      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37269      * @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:
37270 <pre><code>
37271 panel.load({
37272     url: "your-url.php",
37273     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37274     callback: yourFunction,
37275     scope: yourObject, //(optional scope)
37276     discardUrl: false,
37277     nocache: false,
37278     text: "Loading...",
37279     timeout: 30,
37280     scripts: false
37281 });
37282 </code></pre>
37283      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37284      * 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.
37285      * @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}
37286      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37287      * @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.
37288      * @return {Roo.ContentPanel} this
37289      */
37290     load : function(){
37291         var um = this.el.getUpdateManager();
37292         um.update.apply(um, arguments);
37293         return this;
37294     },
37295
37296
37297     /**
37298      * 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.
37299      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37300      * @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)
37301      * @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)
37302      * @return {Roo.UpdateManager} The UpdateManager
37303      */
37304     setUrl : function(url, params, loadOnce){
37305         if(this.refreshDelegate){
37306             this.removeListener("activate", this.refreshDelegate);
37307         }
37308         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37309         this.on("activate", this.refreshDelegate);
37310         return this.el.getUpdateManager();
37311     },
37312     
37313     _handleRefresh : function(url, params, loadOnce){
37314         if(!loadOnce || !this.loaded){
37315             var updater = this.el.getUpdateManager();
37316             updater.update(url, params, this._setLoaded.createDelegate(this));
37317         }
37318     },
37319     
37320     _setLoaded : function(){
37321         this.loaded = true;
37322     }, 
37323     
37324     /**
37325      * Returns this panel's id
37326      * @return {String} 
37327      */
37328     getId : function(){
37329         return this.el.id;
37330     },
37331     
37332     /** 
37333      * Returns this panel's element - used by regiosn to add.
37334      * @return {Roo.Element} 
37335      */
37336     getEl : function(){
37337         return this.wrapEl || this.el;
37338     },
37339     
37340    
37341     
37342     adjustForComponents : function(width, height)
37343     {
37344         //Roo.log('adjustForComponents ');
37345         if(this.resizeEl != this.el){
37346             width -= this.el.getFrameWidth('lr');
37347             height -= this.el.getFrameWidth('tb');
37348         }
37349         if(this.toolbar){
37350             var te = this.toolbar.getEl();
37351             te.setWidth(width);
37352             height -= te.getHeight();
37353         }
37354         if(this.footer){
37355             var te = this.footer.getEl();
37356             te.setWidth(width);
37357             height -= te.getHeight();
37358         }
37359         
37360         
37361         if(this.adjustments){
37362             width += this.adjustments[0];
37363             height += this.adjustments[1];
37364         }
37365         return {"width": width, "height": height};
37366     },
37367     
37368     setSize : function(width, height){
37369         if(this.fitToFrame && !this.ignoreResize(width, height)){
37370             if(this.fitContainer && this.resizeEl != this.el){
37371                 this.el.setSize(width, height);
37372             }
37373             var size = this.adjustForComponents(width, height);
37374             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37375             this.fireEvent('resize', this, size.width, size.height);
37376         }
37377     },
37378     
37379     /**
37380      * Returns this panel's title
37381      * @return {String} 
37382      */
37383     getTitle : function(){
37384         
37385         if (typeof(this.title) != 'object') {
37386             return this.title;
37387         }
37388         
37389         var t = '';
37390         for (var k in this.title) {
37391             if (!this.title.hasOwnProperty(k)) {
37392                 continue;
37393             }
37394             
37395             if (k.indexOf('-') >= 0) {
37396                 var s = k.split('-');
37397                 for (var i = 0; i<s.length; i++) {
37398                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37399                 }
37400             } else {
37401                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37402             }
37403         }
37404         return t;
37405     },
37406     
37407     /**
37408      * Set this panel's title
37409      * @param {String} title
37410      */
37411     setTitle : function(title){
37412         this.title = title;
37413         if(this.region){
37414             this.region.updatePanelTitle(this, title);
37415         }
37416     },
37417     
37418     /**
37419      * Returns true is this panel was configured to be closable
37420      * @return {Boolean} 
37421      */
37422     isClosable : function(){
37423         return this.closable;
37424     },
37425     
37426     beforeSlide : function(){
37427         this.el.clip();
37428         this.resizeEl.clip();
37429     },
37430     
37431     afterSlide : function(){
37432         this.el.unclip();
37433         this.resizeEl.unclip();
37434     },
37435     
37436     /**
37437      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37438      *   Will fail silently if the {@link #setUrl} method has not been called.
37439      *   This does not activate the panel, just updates its content.
37440      */
37441     refresh : function(){
37442         if(this.refreshDelegate){
37443            this.loaded = false;
37444            this.refreshDelegate();
37445         }
37446     },
37447     
37448     /**
37449      * Destroys this panel
37450      */
37451     destroy : function(){
37452         this.el.removeAllListeners();
37453         var tempEl = document.createElement("span");
37454         tempEl.appendChild(this.el.dom);
37455         tempEl.innerHTML = "";
37456         this.el.remove();
37457         this.el = null;
37458     },
37459     
37460     /**
37461      * form - if the content panel contains a form - this is a reference to it.
37462      * @type {Roo.form.Form}
37463      */
37464     form : false,
37465     /**
37466      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37467      *    This contains a reference to it.
37468      * @type {Roo.View}
37469      */
37470     view : false,
37471     
37472       /**
37473      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37474      * <pre><code>
37475
37476 layout.addxtype({
37477        xtype : 'Form',
37478        items: [ .... ]
37479    }
37480 );
37481
37482 </code></pre>
37483      * @param {Object} cfg Xtype definition of item to add.
37484      */
37485     
37486     
37487     getChildContainer: function () {
37488         return this.getEl();
37489     }
37490     
37491     
37492     /*
37493         var  ret = new Roo.factory(cfg);
37494         return ret;
37495         
37496         
37497         // add form..
37498         if (cfg.xtype.match(/^Form$/)) {
37499             
37500             var el;
37501             //if (this.footer) {
37502             //    el = this.footer.container.insertSibling(false, 'before');
37503             //} else {
37504                 el = this.el.createChild();
37505             //}
37506
37507             this.form = new  Roo.form.Form(cfg);
37508             
37509             
37510             if ( this.form.allItems.length) {
37511                 this.form.render(el.dom);
37512             }
37513             return this.form;
37514         }
37515         // should only have one of theses..
37516         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37517             // views.. should not be just added - used named prop 'view''
37518             
37519             cfg.el = this.el.appendChild(document.createElement("div"));
37520             // factory?
37521             
37522             var ret = new Roo.factory(cfg);
37523              
37524              ret.render && ret.render(false, ''); // render blank..
37525             this.view = ret;
37526             return ret;
37527         }
37528         return false;
37529     }
37530     \*/
37531 });
37532  
37533 /**
37534  * @class Roo.bootstrap.panel.Grid
37535  * @extends Roo.bootstrap.panel.Content
37536  * @constructor
37537  * Create a new GridPanel.
37538  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37539  * @param {Object} config A the config object
37540   
37541  */
37542
37543
37544
37545 Roo.bootstrap.panel.Grid = function(config)
37546 {
37547     
37548       
37549     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37550         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37551
37552     config.el = this.wrapper;
37553     //this.el = this.wrapper;
37554     
37555       if (config.container) {
37556         // ctor'ed from a Border/panel.grid
37557         
37558         
37559         this.wrapper.setStyle("overflow", "hidden");
37560         this.wrapper.addClass('roo-grid-container');
37561
37562     }
37563     
37564     
37565     if(config.toolbar){
37566         var tool_el = this.wrapper.createChild();    
37567         this.toolbar = Roo.factory(config.toolbar);
37568         var ti = [];
37569         if (config.toolbar.items) {
37570             ti = config.toolbar.items ;
37571             delete config.toolbar.items ;
37572         }
37573         
37574         var nitems = [];
37575         this.toolbar.render(tool_el);
37576         for(var i =0;i < ti.length;i++) {
37577           //  Roo.log(['add child', items[i]]);
37578             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37579         }
37580         this.toolbar.items = nitems;
37581         
37582         delete config.toolbar;
37583     }
37584     
37585     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37586     config.grid.scrollBody = true;;
37587     config.grid.monitorWindowResize = false; // turn off autosizing
37588     config.grid.autoHeight = false;
37589     config.grid.autoWidth = false;
37590     
37591     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37592     
37593     if (config.background) {
37594         // render grid on panel activation (if panel background)
37595         this.on('activate', function(gp) {
37596             if (!gp.grid.rendered) {
37597                 gp.grid.render(this.wrapper);
37598                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37599             }
37600         });
37601             
37602     } else {
37603         this.grid.render(this.wrapper);
37604         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37605
37606     }
37607     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37608     // ??? needed ??? config.el = this.wrapper;
37609     
37610     
37611     
37612   
37613     // xtype created footer. - not sure if will work as we normally have to render first..
37614     if (this.footer && !this.footer.el && this.footer.xtype) {
37615         
37616         var ctr = this.grid.getView().getFooterPanel(true);
37617         this.footer.dataSource = this.grid.dataSource;
37618         this.footer = Roo.factory(this.footer, Roo);
37619         this.footer.render(ctr);
37620         
37621     }
37622     
37623     
37624     
37625     
37626      
37627 };
37628
37629 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37630     getId : function(){
37631         return this.grid.id;
37632     },
37633     
37634     /**
37635      * Returns the grid for this panel
37636      * @return {Roo.bootstrap.Table} 
37637      */
37638     getGrid : function(){
37639         return this.grid;    
37640     },
37641     
37642     setSize : function(width, height){
37643         if(!this.ignoreResize(width, height)){
37644             var grid = this.grid;
37645             var size = this.adjustForComponents(width, height);
37646             var gridel = grid.getGridEl();
37647             gridel.setSize(size.width, size.height);
37648             /*
37649             var thd = grid.getGridEl().select('thead',true).first();
37650             var tbd = grid.getGridEl().select('tbody', true).first();
37651             if (tbd) {
37652                 tbd.setSize(width, height - thd.getHeight());
37653             }
37654             */
37655             grid.autoSize();
37656         }
37657     },
37658      
37659     
37660     
37661     beforeSlide : function(){
37662         this.grid.getView().scroller.clip();
37663     },
37664     
37665     afterSlide : function(){
37666         this.grid.getView().scroller.unclip();
37667     },
37668     
37669     destroy : function(){
37670         this.grid.destroy();
37671         delete this.grid;
37672         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37673     }
37674 });
37675
37676 /**
37677  * @class Roo.bootstrap.panel.Nest
37678  * @extends Roo.bootstrap.panel.Content
37679  * @constructor
37680  * Create a new Panel, that can contain a layout.Border.
37681  * 
37682  * 
37683  * @param {Roo.BorderLayout} layout The layout for this panel
37684  * @param {String/Object} config A string to set only the title or a config object
37685  */
37686 Roo.bootstrap.panel.Nest = function(config)
37687 {
37688     // construct with only one argument..
37689     /* FIXME - implement nicer consturctors
37690     if (layout.layout) {
37691         config = layout;
37692         layout = config.layout;
37693         delete config.layout;
37694     }
37695     if (layout.xtype && !layout.getEl) {
37696         // then layout needs constructing..
37697         layout = Roo.factory(layout, Roo);
37698     }
37699     */
37700     
37701     config.el =  config.layout.getEl();
37702     
37703     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37704     
37705     config.layout.monitorWindowResize = false; // turn off autosizing
37706     this.layout = config.layout;
37707     this.layout.getEl().addClass("roo-layout-nested-layout");
37708     
37709     
37710     
37711     
37712 };
37713
37714 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37715
37716     setSize : function(width, height){
37717         if(!this.ignoreResize(width, height)){
37718             var size = this.adjustForComponents(width, height);
37719             var el = this.layout.getEl();
37720             if (size.height < 1) {
37721                 el.setWidth(size.width);   
37722             } else {
37723                 el.setSize(size.width, size.height);
37724             }
37725             var touch = el.dom.offsetWidth;
37726             this.layout.layout();
37727             // ie requires a double layout on the first pass
37728             if(Roo.isIE && !this.initialized){
37729                 this.initialized = true;
37730                 this.layout.layout();
37731             }
37732         }
37733     },
37734     
37735     // activate all subpanels if not currently active..
37736     
37737     setActiveState : function(active){
37738         this.active = active;
37739         this.setActiveClass(active);
37740         
37741         if(!active){
37742             this.fireEvent("deactivate", this);
37743             return;
37744         }
37745         
37746         this.fireEvent("activate", this);
37747         // not sure if this should happen before or after..
37748         if (!this.layout) {
37749             return; // should not happen..
37750         }
37751         var reg = false;
37752         for (var r in this.layout.regions) {
37753             reg = this.layout.getRegion(r);
37754             if (reg.getActivePanel()) {
37755                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37756                 reg.setActivePanel(reg.getActivePanel());
37757                 continue;
37758             }
37759             if (!reg.panels.length) {
37760                 continue;
37761             }
37762             reg.showPanel(reg.getPanel(0));
37763         }
37764         
37765         
37766         
37767         
37768     },
37769     
37770     /**
37771      * Returns the nested BorderLayout for this panel
37772      * @return {Roo.BorderLayout} 
37773      */
37774     getLayout : function(){
37775         return this.layout;
37776     },
37777     
37778      /**
37779      * Adds a xtype elements to the layout of the nested panel
37780      * <pre><code>
37781
37782 panel.addxtype({
37783        xtype : 'ContentPanel',
37784        region: 'west',
37785        items: [ .... ]
37786    }
37787 );
37788
37789 panel.addxtype({
37790         xtype : 'NestedLayoutPanel',
37791         region: 'west',
37792         layout: {
37793            center: { },
37794            west: { }   
37795         },
37796         items : [ ... list of content panels or nested layout panels.. ]
37797    }
37798 );
37799 </code></pre>
37800      * @param {Object} cfg Xtype definition of item to add.
37801      */
37802     addxtype : function(cfg) {
37803         return this.layout.addxtype(cfg);
37804     
37805     }
37806 });        /*
37807  * Based on:
37808  * Ext JS Library 1.1.1
37809  * Copyright(c) 2006-2007, Ext JS, LLC.
37810  *
37811  * Originally Released Under LGPL - original licence link has changed is not relivant.
37812  *
37813  * Fork - LGPL
37814  * <script type="text/javascript">
37815  */
37816 /**
37817  * @class Roo.TabPanel
37818  * @extends Roo.util.Observable
37819  * A lightweight tab container.
37820  * <br><br>
37821  * Usage:
37822  * <pre><code>
37823 // basic tabs 1, built from existing content
37824 var tabs = new Roo.TabPanel("tabs1");
37825 tabs.addTab("script", "View Script");
37826 tabs.addTab("markup", "View Markup");
37827 tabs.activate("script");
37828
37829 // more advanced tabs, built from javascript
37830 var jtabs = new Roo.TabPanel("jtabs");
37831 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37832
37833 // set up the UpdateManager
37834 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37835 var updater = tab2.getUpdateManager();
37836 updater.setDefaultUrl("ajax1.htm");
37837 tab2.on('activate', updater.refresh, updater, true);
37838
37839 // Use setUrl for Ajax loading
37840 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37841 tab3.setUrl("ajax2.htm", null, true);
37842
37843 // Disabled tab
37844 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37845 tab4.disable();
37846
37847 jtabs.activate("jtabs-1");
37848  * </code></pre>
37849  * @constructor
37850  * Create a new TabPanel.
37851  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37852  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37853  */
37854 Roo.bootstrap.panel.Tabs = function(config){
37855     /**
37856     * The container element for this TabPanel.
37857     * @type Roo.Element
37858     */
37859     this.el = Roo.get(config.el);
37860     delete config.el;
37861     if(config){
37862         if(typeof config == "boolean"){
37863             this.tabPosition = config ? "bottom" : "top";
37864         }else{
37865             Roo.apply(this, config);
37866         }
37867     }
37868     
37869     if(this.tabPosition == "bottom"){
37870         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37871         this.el.addClass("roo-tabs-bottom");
37872     }
37873     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37874     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37875     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37876     if(Roo.isIE){
37877         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37878     }
37879     if(this.tabPosition != "bottom"){
37880         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37881          * @type Roo.Element
37882          */
37883         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37884         this.el.addClass("roo-tabs-top");
37885     }
37886     this.items = [];
37887
37888     this.bodyEl.setStyle("position", "relative");
37889
37890     this.active = null;
37891     this.activateDelegate = this.activate.createDelegate(this);
37892
37893     this.addEvents({
37894         /**
37895          * @event tabchange
37896          * Fires when the active tab changes
37897          * @param {Roo.TabPanel} this
37898          * @param {Roo.TabPanelItem} activePanel The new active tab
37899          */
37900         "tabchange": true,
37901         /**
37902          * @event beforetabchange
37903          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37904          * @param {Roo.TabPanel} this
37905          * @param {Object} e Set cancel to true on this object to cancel the tab change
37906          * @param {Roo.TabPanelItem} tab The tab being changed to
37907          */
37908         "beforetabchange" : true
37909     });
37910
37911     Roo.EventManager.onWindowResize(this.onResize, this);
37912     this.cpad = this.el.getPadding("lr");
37913     this.hiddenCount = 0;
37914
37915
37916     // toolbar on the tabbar support...
37917     if (this.toolbar) {
37918         alert("no toolbar support yet");
37919         this.toolbar  = false;
37920         /*
37921         var tcfg = this.toolbar;
37922         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37923         this.toolbar = new Roo.Toolbar(tcfg);
37924         if (Roo.isSafari) {
37925             var tbl = tcfg.container.child('table', true);
37926             tbl.setAttribute('width', '100%');
37927         }
37928         */
37929         
37930     }
37931    
37932
37933
37934     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37935 };
37936
37937 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37938     /*
37939      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37940      */
37941     tabPosition : "top",
37942     /*
37943      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37944      */
37945     currentTabWidth : 0,
37946     /*
37947      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37948      */
37949     minTabWidth : 40,
37950     /*
37951      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37952      */
37953     maxTabWidth : 250,
37954     /*
37955      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37956      */
37957     preferredTabWidth : 175,
37958     /*
37959      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37960      */
37961     resizeTabs : false,
37962     /*
37963      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37964      */
37965     monitorResize : true,
37966     /*
37967      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37968      */
37969     toolbar : false,
37970
37971     /**
37972      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37973      * @param {String} id The id of the div to use <b>or create</b>
37974      * @param {String} text The text for the tab
37975      * @param {String} content (optional) Content to put in the TabPanelItem body
37976      * @param {Boolean} closable (optional) True to create a close icon on the tab
37977      * @return {Roo.TabPanelItem} The created TabPanelItem
37978      */
37979     addTab : function(id, text, content, closable, tpl)
37980     {
37981         var item = new Roo.bootstrap.panel.TabItem({
37982             panel: this,
37983             id : id,
37984             text : text,
37985             closable : closable,
37986             tpl : tpl
37987         });
37988         this.addTabItem(item);
37989         if(content){
37990             item.setContent(content);
37991         }
37992         return item;
37993     },
37994
37995     /**
37996      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37997      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37998      * @return {Roo.TabPanelItem}
37999      */
38000     getTab : function(id){
38001         return this.items[id];
38002     },
38003
38004     /**
38005      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38006      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38007      */
38008     hideTab : function(id){
38009         var t = this.items[id];
38010         if(!t.isHidden()){
38011            t.setHidden(true);
38012            this.hiddenCount++;
38013            this.autoSizeTabs();
38014         }
38015     },
38016
38017     /**
38018      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38019      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38020      */
38021     unhideTab : function(id){
38022         var t = this.items[id];
38023         if(t.isHidden()){
38024            t.setHidden(false);
38025            this.hiddenCount--;
38026            this.autoSizeTabs();
38027         }
38028     },
38029
38030     /**
38031      * Adds an existing {@link Roo.TabPanelItem}.
38032      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38033      */
38034     addTabItem : function(item){
38035         this.items[item.id] = item;
38036         this.items.push(item);
38037       //  if(this.resizeTabs){
38038     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38039   //         this.autoSizeTabs();
38040 //        }else{
38041 //            item.autoSize();
38042        // }
38043     },
38044
38045     /**
38046      * Removes a {@link Roo.TabPanelItem}.
38047      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38048      */
38049     removeTab : function(id){
38050         var items = this.items;
38051         var tab = items[id];
38052         if(!tab) { return; }
38053         var index = items.indexOf(tab);
38054         if(this.active == tab && items.length > 1){
38055             var newTab = this.getNextAvailable(index);
38056             if(newTab) {
38057                 newTab.activate();
38058             }
38059         }
38060         this.stripEl.dom.removeChild(tab.pnode.dom);
38061         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38062             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38063         }
38064         items.splice(index, 1);
38065         delete this.items[tab.id];
38066         tab.fireEvent("close", tab);
38067         tab.purgeListeners();
38068         this.autoSizeTabs();
38069     },
38070
38071     getNextAvailable : function(start){
38072         var items = this.items;
38073         var index = start;
38074         // look for a next tab that will slide over to
38075         // replace the one being removed
38076         while(index < items.length){
38077             var item = items[++index];
38078             if(item && !item.isHidden()){
38079                 return item;
38080             }
38081         }
38082         // if one isn't found select the previous tab (on the left)
38083         index = start;
38084         while(index >= 0){
38085             var item = items[--index];
38086             if(item && !item.isHidden()){
38087                 return item;
38088             }
38089         }
38090         return null;
38091     },
38092
38093     /**
38094      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38095      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38096      */
38097     disableTab : function(id){
38098         var tab = this.items[id];
38099         if(tab && this.active != tab){
38100             tab.disable();
38101         }
38102     },
38103
38104     /**
38105      * Enables a {@link Roo.TabPanelItem} that is disabled.
38106      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38107      */
38108     enableTab : function(id){
38109         var tab = this.items[id];
38110         tab.enable();
38111     },
38112
38113     /**
38114      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38115      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38116      * @return {Roo.TabPanelItem} The TabPanelItem.
38117      */
38118     activate : function(id){
38119         var tab = this.items[id];
38120         if(!tab){
38121             return null;
38122         }
38123         if(tab == this.active || tab.disabled){
38124             return tab;
38125         }
38126         var e = {};
38127         this.fireEvent("beforetabchange", this, e, tab);
38128         if(e.cancel !== true && !tab.disabled){
38129             if(this.active){
38130                 this.active.hide();
38131             }
38132             this.active = this.items[id];
38133             this.active.show();
38134             this.fireEvent("tabchange", this, this.active);
38135         }
38136         return tab;
38137     },
38138
38139     /**
38140      * Gets the active {@link Roo.TabPanelItem}.
38141      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38142      */
38143     getActiveTab : function(){
38144         return this.active;
38145     },
38146
38147     /**
38148      * Updates the tab body element to fit the height of the container element
38149      * for overflow scrolling
38150      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38151      */
38152     syncHeight : function(targetHeight){
38153         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38154         var bm = this.bodyEl.getMargins();
38155         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38156         this.bodyEl.setHeight(newHeight);
38157         return newHeight;
38158     },
38159
38160     onResize : function(){
38161         if(this.monitorResize){
38162             this.autoSizeTabs();
38163         }
38164     },
38165
38166     /**
38167      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38168      */
38169     beginUpdate : function(){
38170         this.updating = true;
38171     },
38172
38173     /**
38174      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38175      */
38176     endUpdate : function(){
38177         this.updating = false;
38178         this.autoSizeTabs();
38179     },
38180
38181     /**
38182      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38183      */
38184     autoSizeTabs : function(){
38185         var count = this.items.length;
38186         var vcount = count - this.hiddenCount;
38187         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38188             return;
38189         }
38190         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38191         var availWidth = Math.floor(w / vcount);
38192         var b = this.stripBody;
38193         if(b.getWidth() > w){
38194             var tabs = this.items;
38195             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38196             if(availWidth < this.minTabWidth){
38197                 /*if(!this.sleft){    // incomplete scrolling code
38198                     this.createScrollButtons();
38199                 }
38200                 this.showScroll();
38201                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38202             }
38203         }else{
38204             if(this.currentTabWidth < this.preferredTabWidth){
38205                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38206             }
38207         }
38208     },
38209
38210     /**
38211      * Returns the number of tabs in this TabPanel.
38212      * @return {Number}
38213      */
38214      getCount : function(){
38215          return this.items.length;
38216      },
38217
38218     /**
38219      * Resizes all the tabs to the passed width
38220      * @param {Number} The new width
38221      */
38222     setTabWidth : function(width){
38223         this.currentTabWidth = width;
38224         for(var i = 0, len = this.items.length; i < len; i++) {
38225                 if(!this.items[i].isHidden()) {
38226                 this.items[i].setWidth(width);
38227             }
38228         }
38229     },
38230
38231     /**
38232      * Destroys this TabPanel
38233      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38234      */
38235     destroy : function(removeEl){
38236         Roo.EventManager.removeResizeListener(this.onResize, this);
38237         for(var i = 0, len = this.items.length; i < len; i++){
38238             this.items[i].purgeListeners();
38239         }
38240         if(removeEl === true){
38241             this.el.update("");
38242             this.el.remove();
38243         }
38244     },
38245     
38246     createStrip : function(container)
38247     {
38248         var strip = document.createElement("nav");
38249         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38250         container.appendChild(strip);
38251         return strip;
38252     },
38253     
38254     createStripList : function(strip)
38255     {
38256         // div wrapper for retard IE
38257         // returns the "tr" element.
38258         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38259         //'<div class="x-tabs-strip-wrap">'+
38260           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38261           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38262         return strip.firstChild; //.firstChild.firstChild.firstChild;
38263     },
38264     createBody : function(container)
38265     {
38266         var body = document.createElement("div");
38267         Roo.id(body, "tab-body");
38268         //Roo.fly(body).addClass("x-tabs-body");
38269         Roo.fly(body).addClass("tab-content");
38270         container.appendChild(body);
38271         return body;
38272     },
38273     createItemBody :function(bodyEl, id){
38274         var body = Roo.getDom(id);
38275         if(!body){
38276             body = document.createElement("div");
38277             body.id = id;
38278         }
38279         //Roo.fly(body).addClass("x-tabs-item-body");
38280         Roo.fly(body).addClass("tab-pane");
38281          bodyEl.insertBefore(body, bodyEl.firstChild);
38282         return body;
38283     },
38284     /** @private */
38285     createStripElements :  function(stripEl, text, closable, tpl)
38286     {
38287         var td = document.createElement("li"); // was td..
38288         
38289         
38290         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38291         
38292         
38293         stripEl.appendChild(td);
38294         /*if(closable){
38295             td.className = "x-tabs-closable";
38296             if(!this.closeTpl){
38297                 this.closeTpl = new Roo.Template(
38298                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38299                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38300                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38301                 );
38302             }
38303             var el = this.closeTpl.overwrite(td, {"text": text});
38304             var close = el.getElementsByTagName("div")[0];
38305             var inner = el.getElementsByTagName("em")[0];
38306             return {"el": el, "close": close, "inner": inner};
38307         } else {
38308         */
38309         // not sure what this is..
38310 //            if(!this.tabTpl){
38311                 //this.tabTpl = new Roo.Template(
38312                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38313                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38314                 //);
38315 //                this.tabTpl = new Roo.Template(
38316 //                   '<a href="#">' +
38317 //                   '<span unselectable="on"' +
38318 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38319 //                            ' >{text}</span></a>'
38320 //                );
38321 //                
38322 //            }
38323
38324
38325             var template = tpl || this.tabTpl || false;
38326             
38327             if(!template){
38328                 
38329                 template = new Roo.Template(
38330                    '<a href="#">' +
38331                    '<span unselectable="on"' +
38332                             (this.disableTooltips ? '' : ' title="{text}"') +
38333                             ' >{text}</span></a>'
38334                 );
38335             }
38336             
38337             switch (typeof(template)) {
38338                 case 'object' :
38339                     break;
38340                 case 'string' :
38341                     template = new Roo.Template(template);
38342                     break;
38343                 default :
38344                     break;
38345             }
38346             
38347             var el = template.overwrite(td, {"text": text});
38348             
38349             var inner = el.getElementsByTagName("span")[0];
38350             
38351             return {"el": el, "inner": inner};
38352             
38353     }
38354         
38355     
38356 });
38357
38358 /**
38359  * @class Roo.TabPanelItem
38360  * @extends Roo.util.Observable
38361  * Represents an individual item (tab plus body) in a TabPanel.
38362  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38363  * @param {String} id The id of this TabPanelItem
38364  * @param {String} text The text for the tab of this TabPanelItem
38365  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38366  */
38367 Roo.bootstrap.panel.TabItem = function(config){
38368     /**
38369      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38370      * @type Roo.TabPanel
38371      */
38372     this.tabPanel = config.panel;
38373     /**
38374      * The id for this TabPanelItem
38375      * @type String
38376      */
38377     this.id = config.id;
38378     /** @private */
38379     this.disabled = false;
38380     /** @private */
38381     this.text = config.text;
38382     /** @private */
38383     this.loaded = false;
38384     this.closable = config.closable;
38385
38386     /**
38387      * The body element for this TabPanelItem.
38388      * @type Roo.Element
38389      */
38390     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38391     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38392     this.bodyEl.setStyle("display", "block");
38393     this.bodyEl.setStyle("zoom", "1");
38394     //this.hideAction();
38395
38396     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38397     /** @private */
38398     this.el = Roo.get(els.el);
38399     this.inner = Roo.get(els.inner, true);
38400     this.textEl = Roo.get(this.el.dom.firstChild, true);
38401     this.pnode = Roo.get(els.el.parentNode, true);
38402 //    this.el.on("mousedown", this.onTabMouseDown, this);
38403     this.el.on("click", this.onTabClick, this);
38404     /** @private */
38405     if(config.closable){
38406         var c = Roo.get(els.close, true);
38407         c.dom.title = this.closeText;
38408         c.addClassOnOver("close-over");
38409         c.on("click", this.closeClick, this);
38410      }
38411
38412     this.addEvents({
38413          /**
38414          * @event activate
38415          * Fires when this tab becomes the active tab.
38416          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38417          * @param {Roo.TabPanelItem} this
38418          */
38419         "activate": true,
38420         /**
38421          * @event beforeclose
38422          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38423          * @param {Roo.TabPanelItem} this
38424          * @param {Object} e Set cancel to true on this object to cancel the close.
38425          */
38426         "beforeclose": true,
38427         /**
38428          * @event close
38429          * Fires when this tab is closed.
38430          * @param {Roo.TabPanelItem} this
38431          */
38432          "close": true,
38433         /**
38434          * @event deactivate
38435          * Fires when this tab is no longer the active tab.
38436          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38437          * @param {Roo.TabPanelItem} this
38438          */
38439          "deactivate" : true
38440     });
38441     this.hidden = false;
38442
38443     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38444 };
38445
38446 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38447            {
38448     purgeListeners : function(){
38449        Roo.util.Observable.prototype.purgeListeners.call(this);
38450        this.el.removeAllListeners();
38451     },
38452     /**
38453      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38454      */
38455     show : function(){
38456         this.pnode.addClass("active");
38457         this.showAction();
38458         if(Roo.isOpera){
38459             this.tabPanel.stripWrap.repaint();
38460         }
38461         this.fireEvent("activate", this.tabPanel, this);
38462     },
38463
38464     /**
38465      * Returns true if this tab is the active tab.
38466      * @return {Boolean}
38467      */
38468     isActive : function(){
38469         return this.tabPanel.getActiveTab() == this;
38470     },
38471
38472     /**
38473      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38474      */
38475     hide : function(){
38476         this.pnode.removeClass("active");
38477         this.hideAction();
38478         this.fireEvent("deactivate", this.tabPanel, this);
38479     },
38480
38481     hideAction : function(){
38482         this.bodyEl.hide();
38483         this.bodyEl.setStyle("position", "absolute");
38484         this.bodyEl.setLeft("-20000px");
38485         this.bodyEl.setTop("-20000px");
38486     },
38487
38488     showAction : function(){
38489         this.bodyEl.setStyle("position", "relative");
38490         this.bodyEl.setTop("");
38491         this.bodyEl.setLeft("");
38492         this.bodyEl.show();
38493     },
38494
38495     /**
38496      * Set the tooltip for the tab.
38497      * @param {String} tooltip The tab's tooltip
38498      */
38499     setTooltip : function(text){
38500         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38501             this.textEl.dom.qtip = text;
38502             this.textEl.dom.removeAttribute('title');
38503         }else{
38504             this.textEl.dom.title = text;
38505         }
38506     },
38507
38508     onTabClick : function(e){
38509         e.preventDefault();
38510         this.tabPanel.activate(this.id);
38511     },
38512
38513     onTabMouseDown : function(e){
38514         e.preventDefault();
38515         this.tabPanel.activate(this.id);
38516     },
38517 /*
38518     getWidth : function(){
38519         return this.inner.getWidth();
38520     },
38521
38522     setWidth : function(width){
38523         var iwidth = width - this.pnode.getPadding("lr");
38524         this.inner.setWidth(iwidth);
38525         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38526         this.pnode.setWidth(width);
38527     },
38528 */
38529     /**
38530      * Show or hide the tab
38531      * @param {Boolean} hidden True to hide or false to show.
38532      */
38533     setHidden : function(hidden){
38534         this.hidden = hidden;
38535         this.pnode.setStyle("display", hidden ? "none" : "");
38536     },
38537
38538     /**
38539      * Returns true if this tab is "hidden"
38540      * @return {Boolean}
38541      */
38542     isHidden : function(){
38543         return this.hidden;
38544     },
38545
38546     /**
38547      * Returns the text for this tab
38548      * @return {String}
38549      */
38550     getText : function(){
38551         return this.text;
38552     },
38553     /*
38554     autoSize : function(){
38555         //this.el.beginMeasure();
38556         this.textEl.setWidth(1);
38557         /*
38558          *  #2804 [new] Tabs in Roojs
38559          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38560          */
38561         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38562         //this.el.endMeasure();
38563     //},
38564
38565     /**
38566      * Sets the text for the tab (Note: this also sets the tooltip text)
38567      * @param {String} text The tab's text and tooltip
38568      */
38569     setText : function(text){
38570         this.text = text;
38571         this.textEl.update(text);
38572         this.setTooltip(text);
38573         //if(!this.tabPanel.resizeTabs){
38574         //    this.autoSize();
38575         //}
38576     },
38577     /**
38578      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38579      */
38580     activate : function(){
38581         this.tabPanel.activate(this.id);
38582     },
38583
38584     /**
38585      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38586      */
38587     disable : function(){
38588         if(this.tabPanel.active != this){
38589             this.disabled = true;
38590             this.pnode.addClass("disabled");
38591         }
38592     },
38593
38594     /**
38595      * Enables this TabPanelItem if it was previously disabled.
38596      */
38597     enable : function(){
38598         this.disabled = false;
38599         this.pnode.removeClass("disabled");
38600     },
38601
38602     /**
38603      * Sets the content for this TabPanelItem.
38604      * @param {String} content The content
38605      * @param {Boolean} loadScripts true to look for and load scripts
38606      */
38607     setContent : function(content, loadScripts){
38608         this.bodyEl.update(content, loadScripts);
38609     },
38610
38611     /**
38612      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38613      * @return {Roo.UpdateManager} The UpdateManager
38614      */
38615     getUpdateManager : function(){
38616         return this.bodyEl.getUpdateManager();
38617     },
38618
38619     /**
38620      * Set a URL to be used to load the content for this TabPanelItem.
38621      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38622      * @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)
38623      * @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)
38624      * @return {Roo.UpdateManager} The UpdateManager
38625      */
38626     setUrl : function(url, params, loadOnce){
38627         if(this.refreshDelegate){
38628             this.un('activate', this.refreshDelegate);
38629         }
38630         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38631         this.on("activate", this.refreshDelegate);
38632         return this.bodyEl.getUpdateManager();
38633     },
38634
38635     /** @private */
38636     _handleRefresh : function(url, params, loadOnce){
38637         if(!loadOnce || !this.loaded){
38638             var updater = this.bodyEl.getUpdateManager();
38639             updater.update(url, params, this._setLoaded.createDelegate(this));
38640         }
38641     },
38642
38643     /**
38644      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38645      *   Will fail silently if the setUrl method has not been called.
38646      *   This does not activate the panel, just updates its content.
38647      */
38648     refresh : function(){
38649         if(this.refreshDelegate){
38650            this.loaded = false;
38651            this.refreshDelegate();
38652         }
38653     },
38654
38655     /** @private */
38656     _setLoaded : function(){
38657         this.loaded = true;
38658     },
38659
38660     /** @private */
38661     closeClick : function(e){
38662         var o = {};
38663         e.stopEvent();
38664         this.fireEvent("beforeclose", this, o);
38665         if(o.cancel !== true){
38666             this.tabPanel.removeTab(this.id);
38667         }
38668     },
38669     /**
38670      * The text displayed in the tooltip for the close icon.
38671      * @type String
38672      */
38673     closeText : "Close this tab"
38674 });
38675 /**
38676 *    This script refer to:
38677 *    Title: International Telephone Input
38678 *    Author: Jack O'Connor
38679 *    Code version:  v12.1.12
38680 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38681 **/
38682
38683 Roo.bootstrap.PhoneInputData = function() {
38684     var d = [
38685       [
38686         "Afghanistan (‫افغانستان‬‎)",
38687         "af",
38688         "93"
38689       ],
38690       [
38691         "Albania (Shqipëri)",
38692         "al",
38693         "355"
38694       ],
38695       [
38696         "Algeria (‫الجزائر‬‎)",
38697         "dz",
38698         "213"
38699       ],
38700       [
38701         "American Samoa",
38702         "as",
38703         "1684"
38704       ],
38705       [
38706         "Andorra",
38707         "ad",
38708         "376"
38709       ],
38710       [
38711         "Angola",
38712         "ao",
38713         "244"
38714       ],
38715       [
38716         "Anguilla",
38717         "ai",
38718         "1264"
38719       ],
38720       [
38721         "Antigua and Barbuda",
38722         "ag",
38723         "1268"
38724       ],
38725       [
38726         "Argentina",
38727         "ar",
38728         "54"
38729       ],
38730       [
38731         "Armenia (Հայաստան)",
38732         "am",
38733         "374"
38734       ],
38735       [
38736         "Aruba",
38737         "aw",
38738         "297"
38739       ],
38740       [
38741         "Australia",
38742         "au",
38743         "61",
38744         0
38745       ],
38746       [
38747         "Austria (Österreich)",
38748         "at",
38749         "43"
38750       ],
38751       [
38752         "Azerbaijan (Azərbaycan)",
38753         "az",
38754         "994"
38755       ],
38756       [
38757         "Bahamas",
38758         "bs",
38759         "1242"
38760       ],
38761       [
38762         "Bahrain (‫البحرين‬‎)",
38763         "bh",
38764         "973"
38765       ],
38766       [
38767         "Bangladesh (বাংলাদেশ)",
38768         "bd",
38769         "880"
38770       ],
38771       [
38772         "Barbados",
38773         "bb",
38774         "1246"
38775       ],
38776       [
38777         "Belarus (Беларусь)",
38778         "by",
38779         "375"
38780       ],
38781       [
38782         "Belgium (België)",
38783         "be",
38784         "32"
38785       ],
38786       [
38787         "Belize",
38788         "bz",
38789         "501"
38790       ],
38791       [
38792         "Benin (Bénin)",
38793         "bj",
38794         "229"
38795       ],
38796       [
38797         "Bermuda",
38798         "bm",
38799         "1441"
38800       ],
38801       [
38802         "Bhutan (འབྲུག)",
38803         "bt",
38804         "975"
38805       ],
38806       [
38807         "Bolivia",
38808         "bo",
38809         "591"
38810       ],
38811       [
38812         "Bosnia and Herzegovina (Босна и Херцеговина)",
38813         "ba",
38814         "387"
38815       ],
38816       [
38817         "Botswana",
38818         "bw",
38819         "267"
38820       ],
38821       [
38822         "Brazil (Brasil)",
38823         "br",
38824         "55"
38825       ],
38826       [
38827         "British Indian Ocean Territory",
38828         "io",
38829         "246"
38830       ],
38831       [
38832         "British Virgin Islands",
38833         "vg",
38834         "1284"
38835       ],
38836       [
38837         "Brunei",
38838         "bn",
38839         "673"
38840       ],
38841       [
38842         "Bulgaria (България)",
38843         "bg",
38844         "359"
38845       ],
38846       [
38847         "Burkina Faso",
38848         "bf",
38849         "226"
38850       ],
38851       [
38852         "Burundi (Uburundi)",
38853         "bi",
38854         "257"
38855       ],
38856       [
38857         "Cambodia (កម្ពុជា)",
38858         "kh",
38859         "855"
38860       ],
38861       [
38862         "Cameroon (Cameroun)",
38863         "cm",
38864         "237"
38865       ],
38866       [
38867         "Canada",
38868         "ca",
38869         "1",
38870         1,
38871         ["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"]
38872       ],
38873       [
38874         "Cape Verde (Kabu Verdi)",
38875         "cv",
38876         "238"
38877       ],
38878       [
38879         "Caribbean Netherlands",
38880         "bq",
38881         "599",
38882         1
38883       ],
38884       [
38885         "Cayman Islands",
38886         "ky",
38887         "1345"
38888       ],
38889       [
38890         "Central African Republic (République centrafricaine)",
38891         "cf",
38892         "236"
38893       ],
38894       [
38895         "Chad (Tchad)",
38896         "td",
38897         "235"
38898       ],
38899       [
38900         "Chile",
38901         "cl",
38902         "56"
38903       ],
38904       [
38905         "China (中国)",
38906         "cn",
38907         "86"
38908       ],
38909       [
38910         "Christmas Island",
38911         "cx",
38912         "61",
38913         2
38914       ],
38915       [
38916         "Cocos (Keeling) Islands",
38917         "cc",
38918         "61",
38919         1
38920       ],
38921       [
38922         "Colombia",
38923         "co",
38924         "57"
38925       ],
38926       [
38927         "Comoros (‫جزر القمر‬‎)",
38928         "km",
38929         "269"
38930       ],
38931       [
38932         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38933         "cd",
38934         "243"
38935       ],
38936       [
38937         "Congo (Republic) (Congo-Brazzaville)",
38938         "cg",
38939         "242"
38940       ],
38941       [
38942         "Cook Islands",
38943         "ck",
38944         "682"
38945       ],
38946       [
38947         "Costa Rica",
38948         "cr",
38949         "506"
38950       ],
38951       [
38952         "Côte d’Ivoire",
38953         "ci",
38954         "225"
38955       ],
38956       [
38957         "Croatia (Hrvatska)",
38958         "hr",
38959         "385"
38960       ],
38961       [
38962         "Cuba",
38963         "cu",
38964         "53"
38965       ],
38966       [
38967         "Curaçao",
38968         "cw",
38969         "599",
38970         0
38971       ],
38972       [
38973         "Cyprus (Κύπρος)",
38974         "cy",
38975         "357"
38976       ],
38977       [
38978         "Czech Republic (Česká republika)",
38979         "cz",
38980         "420"
38981       ],
38982       [
38983         "Denmark (Danmark)",
38984         "dk",
38985         "45"
38986       ],
38987       [
38988         "Djibouti",
38989         "dj",
38990         "253"
38991       ],
38992       [
38993         "Dominica",
38994         "dm",
38995         "1767"
38996       ],
38997       [
38998         "Dominican Republic (República Dominicana)",
38999         "do",
39000         "1",
39001         2,
39002         ["809", "829", "849"]
39003       ],
39004       [
39005         "Ecuador",
39006         "ec",
39007         "593"
39008       ],
39009       [
39010         "Egypt (‫مصر‬‎)",
39011         "eg",
39012         "20"
39013       ],
39014       [
39015         "El Salvador",
39016         "sv",
39017         "503"
39018       ],
39019       [
39020         "Equatorial Guinea (Guinea Ecuatorial)",
39021         "gq",
39022         "240"
39023       ],
39024       [
39025         "Eritrea",
39026         "er",
39027         "291"
39028       ],
39029       [
39030         "Estonia (Eesti)",
39031         "ee",
39032         "372"
39033       ],
39034       [
39035         "Ethiopia",
39036         "et",
39037         "251"
39038       ],
39039       [
39040         "Falkland Islands (Islas Malvinas)",
39041         "fk",
39042         "500"
39043       ],
39044       [
39045         "Faroe Islands (Føroyar)",
39046         "fo",
39047         "298"
39048       ],
39049       [
39050         "Fiji",
39051         "fj",
39052         "679"
39053       ],
39054       [
39055         "Finland (Suomi)",
39056         "fi",
39057         "358",
39058         0
39059       ],
39060       [
39061         "France",
39062         "fr",
39063         "33"
39064       ],
39065       [
39066         "French Guiana (Guyane française)",
39067         "gf",
39068         "594"
39069       ],
39070       [
39071         "French Polynesia (Polynésie française)",
39072         "pf",
39073         "689"
39074       ],
39075       [
39076         "Gabon",
39077         "ga",
39078         "241"
39079       ],
39080       [
39081         "Gambia",
39082         "gm",
39083         "220"
39084       ],
39085       [
39086         "Georgia (საქართველო)",
39087         "ge",
39088         "995"
39089       ],
39090       [
39091         "Germany (Deutschland)",
39092         "de",
39093         "49"
39094       ],
39095       [
39096         "Ghana (Gaana)",
39097         "gh",
39098         "233"
39099       ],
39100       [
39101         "Gibraltar",
39102         "gi",
39103         "350"
39104       ],
39105       [
39106         "Greece (Ελλάδα)",
39107         "gr",
39108         "30"
39109       ],
39110       [
39111         "Greenland (Kalaallit Nunaat)",
39112         "gl",
39113         "299"
39114       ],
39115       [
39116         "Grenada",
39117         "gd",
39118         "1473"
39119       ],
39120       [
39121         "Guadeloupe",
39122         "gp",
39123         "590",
39124         0
39125       ],
39126       [
39127         "Guam",
39128         "gu",
39129         "1671"
39130       ],
39131       [
39132         "Guatemala",
39133         "gt",
39134         "502"
39135       ],
39136       [
39137         "Guernsey",
39138         "gg",
39139         "44",
39140         1
39141       ],
39142       [
39143         "Guinea (Guinée)",
39144         "gn",
39145         "224"
39146       ],
39147       [
39148         "Guinea-Bissau (Guiné Bissau)",
39149         "gw",
39150         "245"
39151       ],
39152       [
39153         "Guyana",
39154         "gy",
39155         "592"
39156       ],
39157       [
39158         "Haiti",
39159         "ht",
39160         "509"
39161       ],
39162       [
39163         "Honduras",
39164         "hn",
39165         "504"
39166       ],
39167       [
39168         "Hong Kong (香港)",
39169         "hk",
39170         "852"
39171       ],
39172       [
39173         "Hungary (Magyarország)",
39174         "hu",
39175         "36"
39176       ],
39177       [
39178         "Iceland (Ísland)",
39179         "is",
39180         "354"
39181       ],
39182       [
39183         "India (भारत)",
39184         "in",
39185         "91"
39186       ],
39187       [
39188         "Indonesia",
39189         "id",
39190         "62"
39191       ],
39192       [
39193         "Iran (‫ایران‬‎)",
39194         "ir",
39195         "98"
39196       ],
39197       [
39198         "Iraq (‫العراق‬‎)",
39199         "iq",
39200         "964"
39201       ],
39202       [
39203         "Ireland",
39204         "ie",
39205         "353"
39206       ],
39207       [
39208         "Isle of Man",
39209         "im",
39210         "44",
39211         2
39212       ],
39213       [
39214         "Israel (‫ישראל‬‎)",
39215         "il",
39216         "972"
39217       ],
39218       [
39219         "Italy (Italia)",
39220         "it",
39221         "39",
39222         0
39223       ],
39224       [
39225         "Jamaica",
39226         "jm",
39227         "1876"
39228       ],
39229       [
39230         "Japan (日本)",
39231         "jp",
39232         "81"
39233       ],
39234       [
39235         "Jersey",
39236         "je",
39237         "44",
39238         3
39239       ],
39240       [
39241         "Jordan (‫الأردن‬‎)",
39242         "jo",
39243         "962"
39244       ],
39245       [
39246         "Kazakhstan (Казахстан)",
39247         "kz",
39248         "7",
39249         1
39250       ],
39251       [
39252         "Kenya",
39253         "ke",
39254         "254"
39255       ],
39256       [
39257         "Kiribati",
39258         "ki",
39259         "686"
39260       ],
39261       [
39262         "Kosovo",
39263         "xk",
39264         "383"
39265       ],
39266       [
39267         "Kuwait (‫الكويت‬‎)",
39268         "kw",
39269         "965"
39270       ],
39271       [
39272         "Kyrgyzstan (Кыргызстан)",
39273         "kg",
39274         "996"
39275       ],
39276       [
39277         "Laos (ລາວ)",
39278         "la",
39279         "856"
39280       ],
39281       [
39282         "Latvia (Latvija)",
39283         "lv",
39284         "371"
39285       ],
39286       [
39287         "Lebanon (‫لبنان‬‎)",
39288         "lb",
39289         "961"
39290       ],
39291       [
39292         "Lesotho",
39293         "ls",
39294         "266"
39295       ],
39296       [
39297         "Liberia",
39298         "lr",
39299         "231"
39300       ],
39301       [
39302         "Libya (‫ليبيا‬‎)",
39303         "ly",
39304         "218"
39305       ],
39306       [
39307         "Liechtenstein",
39308         "li",
39309         "423"
39310       ],
39311       [
39312         "Lithuania (Lietuva)",
39313         "lt",
39314         "370"
39315       ],
39316       [
39317         "Luxembourg",
39318         "lu",
39319         "352"
39320       ],
39321       [
39322         "Macau (澳門)",
39323         "mo",
39324         "853"
39325       ],
39326       [
39327         "Macedonia (FYROM) (Македонија)",
39328         "mk",
39329         "389"
39330       ],
39331       [
39332         "Madagascar (Madagasikara)",
39333         "mg",
39334         "261"
39335       ],
39336       [
39337         "Malawi",
39338         "mw",
39339         "265"
39340       ],
39341       [
39342         "Malaysia",
39343         "my",
39344         "60"
39345       ],
39346       [
39347         "Maldives",
39348         "mv",
39349         "960"
39350       ],
39351       [
39352         "Mali",
39353         "ml",
39354         "223"
39355       ],
39356       [
39357         "Malta",
39358         "mt",
39359         "356"
39360       ],
39361       [
39362         "Marshall Islands",
39363         "mh",
39364         "692"
39365       ],
39366       [
39367         "Martinique",
39368         "mq",
39369         "596"
39370       ],
39371       [
39372         "Mauritania (‫موريتانيا‬‎)",
39373         "mr",
39374         "222"
39375       ],
39376       [
39377         "Mauritius (Moris)",
39378         "mu",
39379         "230"
39380       ],
39381       [
39382         "Mayotte",
39383         "yt",
39384         "262",
39385         1
39386       ],
39387       [
39388         "Mexico (México)",
39389         "mx",
39390         "52"
39391       ],
39392       [
39393         "Micronesia",
39394         "fm",
39395         "691"
39396       ],
39397       [
39398         "Moldova (Republica Moldova)",
39399         "md",
39400         "373"
39401       ],
39402       [
39403         "Monaco",
39404         "mc",
39405         "377"
39406       ],
39407       [
39408         "Mongolia (Монгол)",
39409         "mn",
39410         "976"
39411       ],
39412       [
39413         "Montenegro (Crna Gora)",
39414         "me",
39415         "382"
39416       ],
39417       [
39418         "Montserrat",
39419         "ms",
39420         "1664"
39421       ],
39422       [
39423         "Morocco (‫المغرب‬‎)",
39424         "ma",
39425         "212",
39426         0
39427       ],
39428       [
39429         "Mozambique (Moçambique)",
39430         "mz",
39431         "258"
39432       ],
39433       [
39434         "Myanmar (Burma) (မြန်မာ)",
39435         "mm",
39436         "95"
39437       ],
39438       [
39439         "Namibia (Namibië)",
39440         "na",
39441         "264"
39442       ],
39443       [
39444         "Nauru",
39445         "nr",
39446         "674"
39447       ],
39448       [
39449         "Nepal (नेपाल)",
39450         "np",
39451         "977"
39452       ],
39453       [
39454         "Netherlands (Nederland)",
39455         "nl",
39456         "31"
39457       ],
39458       [
39459         "New Caledonia (Nouvelle-Calédonie)",
39460         "nc",
39461         "687"
39462       ],
39463       [
39464         "New Zealand",
39465         "nz",
39466         "64"
39467       ],
39468       [
39469         "Nicaragua",
39470         "ni",
39471         "505"
39472       ],
39473       [
39474         "Niger (Nijar)",
39475         "ne",
39476         "227"
39477       ],
39478       [
39479         "Nigeria",
39480         "ng",
39481         "234"
39482       ],
39483       [
39484         "Niue",
39485         "nu",
39486         "683"
39487       ],
39488       [
39489         "Norfolk Island",
39490         "nf",
39491         "672"
39492       ],
39493       [
39494         "North Korea (조선 민주주의 인민 공화국)",
39495         "kp",
39496         "850"
39497       ],
39498       [
39499         "Northern Mariana Islands",
39500         "mp",
39501         "1670"
39502       ],
39503       [
39504         "Norway (Norge)",
39505         "no",
39506         "47",
39507         0
39508       ],
39509       [
39510         "Oman (‫عُمان‬‎)",
39511         "om",
39512         "968"
39513       ],
39514       [
39515         "Pakistan (‫پاکستان‬‎)",
39516         "pk",
39517         "92"
39518       ],
39519       [
39520         "Palau",
39521         "pw",
39522         "680"
39523       ],
39524       [
39525         "Palestine (‫فلسطين‬‎)",
39526         "ps",
39527         "970"
39528       ],
39529       [
39530         "Panama (Panamá)",
39531         "pa",
39532         "507"
39533       ],
39534       [
39535         "Papua New Guinea",
39536         "pg",
39537         "675"
39538       ],
39539       [
39540         "Paraguay",
39541         "py",
39542         "595"
39543       ],
39544       [
39545         "Peru (Perú)",
39546         "pe",
39547         "51"
39548       ],
39549       [
39550         "Philippines",
39551         "ph",
39552         "63"
39553       ],
39554       [
39555         "Poland (Polska)",
39556         "pl",
39557         "48"
39558       ],
39559       [
39560         "Portugal",
39561         "pt",
39562         "351"
39563       ],
39564       [
39565         "Puerto Rico",
39566         "pr",
39567         "1",
39568         3,
39569         ["787", "939"]
39570       ],
39571       [
39572         "Qatar (‫قطر‬‎)",
39573         "qa",
39574         "974"
39575       ],
39576       [
39577         "Réunion (La Réunion)",
39578         "re",
39579         "262",
39580         0
39581       ],
39582       [
39583         "Romania (România)",
39584         "ro",
39585         "40"
39586       ],
39587       [
39588         "Russia (Россия)",
39589         "ru",
39590         "7",
39591         0
39592       ],
39593       [
39594         "Rwanda",
39595         "rw",
39596         "250"
39597       ],
39598       [
39599         "Saint Barthélemy",
39600         "bl",
39601         "590",
39602         1
39603       ],
39604       [
39605         "Saint Helena",
39606         "sh",
39607         "290"
39608       ],
39609       [
39610         "Saint Kitts and Nevis",
39611         "kn",
39612         "1869"
39613       ],
39614       [
39615         "Saint Lucia",
39616         "lc",
39617         "1758"
39618       ],
39619       [
39620         "Saint Martin (Saint-Martin (partie française))",
39621         "mf",
39622         "590",
39623         2
39624       ],
39625       [
39626         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39627         "pm",
39628         "508"
39629       ],
39630       [
39631         "Saint Vincent and the Grenadines",
39632         "vc",
39633         "1784"
39634       ],
39635       [
39636         "Samoa",
39637         "ws",
39638         "685"
39639       ],
39640       [
39641         "San Marino",
39642         "sm",
39643         "378"
39644       ],
39645       [
39646         "São Tomé and Príncipe (São Tomé e Príncipe)",
39647         "st",
39648         "239"
39649       ],
39650       [
39651         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39652         "sa",
39653         "966"
39654       ],
39655       [
39656         "Senegal (Sénégal)",
39657         "sn",
39658         "221"
39659       ],
39660       [
39661         "Serbia (Србија)",
39662         "rs",
39663         "381"
39664       ],
39665       [
39666         "Seychelles",
39667         "sc",
39668         "248"
39669       ],
39670       [
39671         "Sierra Leone",
39672         "sl",
39673         "232"
39674       ],
39675       [
39676         "Singapore",
39677         "sg",
39678         "65"
39679       ],
39680       [
39681         "Sint Maarten",
39682         "sx",
39683         "1721"
39684       ],
39685       [
39686         "Slovakia (Slovensko)",
39687         "sk",
39688         "421"
39689       ],
39690       [
39691         "Slovenia (Slovenija)",
39692         "si",
39693         "386"
39694       ],
39695       [
39696         "Solomon Islands",
39697         "sb",
39698         "677"
39699       ],
39700       [
39701         "Somalia (Soomaaliya)",
39702         "so",
39703         "252"
39704       ],
39705       [
39706         "South Africa",
39707         "za",
39708         "27"
39709       ],
39710       [
39711         "South Korea (대한민국)",
39712         "kr",
39713         "82"
39714       ],
39715       [
39716         "South Sudan (‫جنوب السودان‬‎)",
39717         "ss",
39718         "211"
39719       ],
39720       [
39721         "Spain (España)",
39722         "es",
39723         "34"
39724       ],
39725       [
39726         "Sri Lanka (ශ්‍රී ලංකාව)",
39727         "lk",
39728         "94"
39729       ],
39730       [
39731         "Sudan (‫السودان‬‎)",
39732         "sd",
39733         "249"
39734       ],
39735       [
39736         "Suriname",
39737         "sr",
39738         "597"
39739       ],
39740       [
39741         "Svalbard and Jan Mayen",
39742         "sj",
39743         "47",
39744         1
39745       ],
39746       [
39747         "Swaziland",
39748         "sz",
39749         "268"
39750       ],
39751       [
39752         "Sweden (Sverige)",
39753         "se",
39754         "46"
39755       ],
39756       [
39757         "Switzerland (Schweiz)",
39758         "ch",
39759         "41"
39760       ],
39761       [
39762         "Syria (‫سوريا‬‎)",
39763         "sy",
39764         "963"
39765       ],
39766       [
39767         "Taiwan (台灣)",
39768         "tw",
39769         "886"
39770       ],
39771       [
39772         "Tajikistan",
39773         "tj",
39774         "992"
39775       ],
39776       [
39777         "Tanzania",
39778         "tz",
39779         "255"
39780       ],
39781       [
39782         "Thailand (ไทย)",
39783         "th",
39784         "66"
39785       ],
39786       [
39787         "Timor-Leste",
39788         "tl",
39789         "670"
39790       ],
39791       [
39792         "Togo",
39793         "tg",
39794         "228"
39795       ],
39796       [
39797         "Tokelau",
39798         "tk",
39799         "690"
39800       ],
39801       [
39802         "Tonga",
39803         "to",
39804         "676"
39805       ],
39806       [
39807         "Trinidad and Tobago",
39808         "tt",
39809         "1868"
39810       ],
39811       [
39812         "Tunisia (‫تونس‬‎)",
39813         "tn",
39814         "216"
39815       ],
39816       [
39817         "Turkey (Türkiye)",
39818         "tr",
39819         "90"
39820       ],
39821       [
39822         "Turkmenistan",
39823         "tm",
39824         "993"
39825       ],
39826       [
39827         "Turks and Caicos Islands",
39828         "tc",
39829         "1649"
39830       ],
39831       [
39832         "Tuvalu",
39833         "tv",
39834         "688"
39835       ],
39836       [
39837         "U.S. Virgin Islands",
39838         "vi",
39839         "1340"
39840       ],
39841       [
39842         "Uganda",
39843         "ug",
39844         "256"
39845       ],
39846       [
39847         "Ukraine (Україна)",
39848         "ua",
39849         "380"
39850       ],
39851       [
39852         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39853         "ae",
39854         "971"
39855       ],
39856       [
39857         "United Kingdom",
39858         "gb",
39859         "44",
39860         0
39861       ],
39862       [
39863         "United States",
39864         "us",
39865         "1",
39866         0
39867       ],
39868       [
39869         "Uruguay",
39870         "uy",
39871         "598"
39872       ],
39873       [
39874         "Uzbekistan (Oʻzbekiston)",
39875         "uz",
39876         "998"
39877       ],
39878       [
39879         "Vanuatu",
39880         "vu",
39881         "678"
39882       ],
39883       [
39884         "Vatican City (Città del Vaticano)",
39885         "va",
39886         "39",
39887         1
39888       ],
39889       [
39890         "Venezuela",
39891         "ve",
39892         "58"
39893       ],
39894       [
39895         "Vietnam (Việt Nam)",
39896         "vn",
39897         "84"
39898       ],
39899       [
39900         "Wallis and Futuna (Wallis-et-Futuna)",
39901         "wf",
39902         "681"
39903       ],
39904       [
39905         "Western Sahara (‫الصحراء الغربية‬‎)",
39906         "eh",
39907         "212",
39908         1
39909       ],
39910       [
39911         "Yemen (‫اليمن‬‎)",
39912         "ye",
39913         "967"
39914       ],
39915       [
39916         "Zambia",
39917         "zm",
39918         "260"
39919       ],
39920       [
39921         "Zimbabwe",
39922         "zw",
39923         "263"
39924       ],
39925       [
39926         "Åland Islands",
39927         "ax",
39928         "358",
39929         1
39930       ]
39931   ];
39932   
39933   return d;
39934 }/**
39935 *    This script refer to:
39936 *    Title: International Telephone Input
39937 *    Author: Jack O'Connor
39938 *    Code version:  v12.1.12
39939 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39940 **/
39941
39942 /**
39943  * @class Roo.bootstrap.PhoneInput
39944  * @extends Roo.bootstrap.TriggerField
39945  * An input with International dial-code selection
39946  
39947  * @cfg {String} defaultDialCode default '+852'
39948  * @cfg {Array} preferedCountries default []
39949   
39950  * @constructor
39951  * Create a new PhoneInput.
39952  * @param {Object} config Configuration options
39953  */
39954
39955 Roo.bootstrap.PhoneInput = function(config) {
39956     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39957 };
39958
39959 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39960         
39961         listWidth: undefined,
39962         
39963         selectedClass: 'active',
39964         
39965         invalidClass : "has-warning",
39966         
39967         validClass: 'has-success',
39968         
39969         allowed: '0123456789',
39970         
39971         max_length: 15,
39972         
39973         /**
39974          * @cfg {String} defaultDialCode The default dial code when initializing the input
39975          */
39976         defaultDialCode: '+852',
39977         
39978         /**
39979          * @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
39980          */
39981         preferedCountries: false,
39982         
39983         getAutoCreate : function()
39984         {
39985             var data = Roo.bootstrap.PhoneInputData();
39986             var align = this.labelAlign || this.parentLabelAlign();
39987             var id = Roo.id();
39988             
39989             this.allCountries = [];
39990             this.dialCodeMapping = [];
39991             
39992             for (var i = 0; i < data.length; i++) {
39993               var c = data[i];
39994               this.allCountries[i] = {
39995                 name: c[0],
39996                 iso2: c[1],
39997                 dialCode: c[2],
39998                 priority: c[3] || 0,
39999                 areaCodes: c[4] || null
40000               };
40001               this.dialCodeMapping[c[2]] = {
40002                   name: c[0],
40003                   iso2: c[1],
40004                   priority: c[3] || 0,
40005                   areaCodes: c[4] || null
40006               };
40007             }
40008             
40009             var cfg = {
40010                 cls: 'form-group',
40011                 cn: []
40012             };
40013             
40014             var input =  {
40015                 tag: 'input',
40016                 id : id,
40017                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40018                 maxlength: this.max_length,
40019                 cls : 'form-control tel-input',
40020                 autocomplete: 'new-password'
40021             };
40022             
40023             var hiddenInput = {
40024                 tag: 'input',
40025                 type: 'hidden',
40026                 cls: 'hidden-tel-input'
40027             };
40028             
40029             if (this.name) {
40030                 hiddenInput.name = this.name;
40031             }
40032             
40033             if (this.disabled) {
40034                 input.disabled = true;
40035             }
40036             
40037             var flag_container = {
40038                 tag: 'div',
40039                 cls: 'flag-box',
40040                 cn: [
40041                     {
40042                         tag: 'div',
40043                         cls: 'flag'
40044                     },
40045                     {
40046                         tag: 'div',
40047                         cls: 'caret'
40048                     }
40049                 ]
40050             };
40051             
40052             var box = {
40053                 tag: 'div',
40054                 cls: this.hasFeedback ? 'has-feedback' : '',
40055                 cn: [
40056                     hiddenInput,
40057                     input,
40058                     {
40059                         tag: 'input',
40060                         cls: 'dial-code-holder',
40061                         disabled: true
40062                     }
40063                 ]
40064             };
40065             
40066             var container = {
40067                 cls: 'roo-select2-container input-group',
40068                 cn: [
40069                     flag_container,
40070                     box
40071                 ]
40072             };
40073             
40074             if (this.fieldLabel.length) {
40075                 var indicator = {
40076                     tag: 'i',
40077                     tooltip: 'This field is required'
40078                 };
40079                 
40080                 var label = {
40081                     tag: 'label',
40082                     'for':  id,
40083                     cls: 'control-label',
40084                     cn: []
40085                 };
40086                 
40087                 var label_text = {
40088                     tag: 'span',
40089                     html: this.fieldLabel
40090                 };
40091                 
40092                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40093                 label.cn = [
40094                     indicator,
40095                     label_text
40096                 ];
40097                 
40098                 if(this.indicatorpos == 'right') {
40099                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40100                     label.cn = [
40101                         label_text,
40102                         indicator
40103                     ];
40104                 }
40105                 
40106                 if(align == 'left') {
40107                     container = {
40108                         tag: 'div',
40109                         cn: [
40110                             container
40111                         ]
40112                     };
40113                     
40114                     if(this.labelWidth > 12){
40115                         label.style = "width: " + this.labelWidth + 'px';
40116                     }
40117                     if(this.labelWidth < 13 && this.labelmd == 0){
40118                         this.labelmd = this.labelWidth;
40119                     }
40120                     if(this.labellg > 0){
40121                         label.cls += ' col-lg-' + this.labellg;
40122                         input.cls += ' col-lg-' + (12 - this.labellg);
40123                     }
40124                     if(this.labelmd > 0){
40125                         label.cls += ' col-md-' + this.labelmd;
40126                         container.cls += ' col-md-' + (12 - this.labelmd);
40127                     }
40128                     if(this.labelsm > 0){
40129                         label.cls += ' col-sm-' + this.labelsm;
40130                         container.cls += ' col-sm-' + (12 - this.labelsm);
40131                     }
40132                     if(this.labelxs > 0){
40133                         label.cls += ' col-xs-' + this.labelxs;
40134                         container.cls += ' col-xs-' + (12 - this.labelxs);
40135                     }
40136                 }
40137             }
40138             
40139             cfg.cn = [
40140                 label,
40141                 container
40142             ];
40143             
40144             var settings = this;
40145             
40146             ['xs','sm','md','lg'].map(function(size){
40147                 if (settings[size]) {
40148                     cfg.cls += ' col-' + size + '-' + settings[size];
40149                 }
40150             });
40151             
40152             this.store = new Roo.data.Store({
40153                 proxy : new Roo.data.MemoryProxy({}),
40154                 reader : new Roo.data.JsonReader({
40155                     fields : [
40156                         {
40157                             'name' : 'name',
40158                             'type' : 'string'
40159                         },
40160                         {
40161                             'name' : 'iso2',
40162                             'type' : 'string'
40163                         },
40164                         {
40165                             'name' : 'dialCode',
40166                             'type' : 'string'
40167                         },
40168                         {
40169                             'name' : 'priority',
40170                             'type' : 'string'
40171                         },
40172                         {
40173                             'name' : 'areaCodes',
40174                             'type' : 'string'
40175                         }
40176                     ]
40177                 })
40178             });
40179             
40180             if(!this.preferedCountries) {
40181                 this.preferedCountries = [
40182                     'hk',
40183                     'gb',
40184                     'us'
40185                 ];
40186             }
40187             
40188             var p = this.preferedCountries.reverse();
40189             
40190             if(p) {
40191                 for (var i = 0; i < p.length; i++) {
40192                     for (var j = 0; j < this.allCountries.length; j++) {
40193                         if(this.allCountries[j].iso2 == p[i]) {
40194                             var t = this.allCountries[j];
40195                             this.allCountries.splice(j,1);
40196                             this.allCountries.unshift(t);
40197                         }
40198                     } 
40199                 }
40200             }
40201             
40202             this.store.proxy.data = {
40203                 success: true,
40204                 data: this.allCountries
40205             };
40206             
40207             return cfg;
40208         },
40209         
40210         initEvents : function()
40211         {
40212             this.createList();
40213             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40214             
40215             this.indicator = this.indicatorEl();
40216             this.flag = this.flagEl();
40217             this.dialCodeHolder = this.dialCodeHolderEl();
40218             
40219             this.trigger = this.el.select('div.flag-box',true).first();
40220             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40221             
40222             var _this = this;
40223             
40224             (function(){
40225                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40226                 _this.list.setWidth(lw);
40227             }).defer(100);
40228             
40229             this.list.on('mouseover', this.onViewOver, this);
40230             this.list.on('mousemove', this.onViewMove, this);
40231             this.inputEl().on("keyup", this.onKeyUp, this);
40232             this.inputEl().on("keypress", this.onKeyPress, this);
40233             
40234             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40235
40236             this.view = new Roo.View(this.list, this.tpl, {
40237                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40238             });
40239             
40240             this.view.on('click', this.onViewClick, this);
40241             this.setValue(this.defaultDialCode);
40242         },
40243         
40244         onTriggerClick : function(e)
40245         {
40246             Roo.log('trigger click');
40247             if(this.disabled){
40248                 return;
40249             }
40250             
40251             if(this.isExpanded()){
40252                 this.collapse();
40253                 this.hasFocus = false;
40254             }else {
40255                 this.store.load({});
40256                 this.hasFocus = true;
40257                 this.expand();
40258             }
40259         },
40260         
40261         isExpanded : function()
40262         {
40263             return this.list.isVisible();
40264         },
40265         
40266         collapse : function()
40267         {
40268             if(!this.isExpanded()){
40269                 return;
40270             }
40271             this.list.hide();
40272             Roo.get(document).un('mousedown', this.collapseIf, this);
40273             Roo.get(document).un('mousewheel', this.collapseIf, this);
40274             this.fireEvent('collapse', this);
40275             this.validate();
40276         },
40277         
40278         expand : function()
40279         {
40280             Roo.log('expand');
40281
40282             if(this.isExpanded() || !this.hasFocus){
40283                 return;
40284             }
40285             
40286             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40287             this.list.setWidth(lw);
40288             
40289             this.list.show();
40290             this.restrictHeight();
40291             
40292             Roo.get(document).on('mousedown', this.collapseIf, this);
40293             Roo.get(document).on('mousewheel', this.collapseIf, this);
40294             
40295             this.fireEvent('expand', this);
40296         },
40297         
40298         restrictHeight : function()
40299         {
40300             this.list.alignTo(this.inputEl(), this.listAlign);
40301             this.list.alignTo(this.inputEl(), this.listAlign);
40302         },
40303         
40304         onViewOver : function(e, t)
40305         {
40306             if(this.inKeyMode){
40307                 return;
40308             }
40309             var item = this.view.findItemFromChild(t);
40310             
40311             if(item){
40312                 var index = this.view.indexOf(item);
40313                 this.select(index, false);
40314             }
40315         },
40316
40317         // private
40318         onViewClick : function(view, doFocus, el, e)
40319         {
40320             var index = this.view.getSelectedIndexes()[0];
40321             
40322             var r = this.store.getAt(index);
40323             
40324             if(r){
40325                 this.onSelect(r, index);
40326             }
40327             if(doFocus !== false && !this.blockFocus){
40328                 this.inputEl().focus();
40329             }
40330         },
40331         
40332         onViewMove : function(e, t)
40333         {
40334             this.inKeyMode = false;
40335         },
40336         
40337         select : function(index, scrollIntoView)
40338         {
40339             this.selectedIndex = index;
40340             this.view.select(index);
40341             if(scrollIntoView !== false){
40342                 var el = this.view.getNode(index);
40343                 if(el){
40344                     this.list.scrollChildIntoView(el, false);
40345                 }
40346             }
40347         },
40348         
40349         createList : function()
40350         {
40351             this.list = Roo.get(document.body).createChild({
40352                 tag: 'ul',
40353                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40354                 style: 'display:none'
40355             });
40356             
40357             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40358         },
40359         
40360         collapseIf : function(e)
40361         {
40362             var in_combo  = e.within(this.el);
40363             var in_list =  e.within(this.list);
40364             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40365             
40366             if (in_combo || in_list || is_list) {
40367                 return;
40368             }
40369             this.collapse();
40370         },
40371         
40372         onSelect : function(record, index)
40373         {
40374             if(this.fireEvent('beforeselect', this, record, index) !== false){
40375                 
40376                 this.setFlagClass(record.data.iso2);
40377                 this.setDialCode(record.data.dialCode);
40378                 this.hasFocus = false;
40379                 this.collapse();
40380                 this.fireEvent('select', this, record, index);
40381             }
40382         },
40383         
40384         flagEl : function()
40385         {
40386             var flag = this.el.select('div.flag',true).first();
40387             if(!flag){
40388                 return false;
40389             }
40390             return flag;
40391         },
40392         
40393         dialCodeHolderEl : function()
40394         {
40395             var d = this.el.select('input.dial-code-holder',true).first();
40396             if(!d){
40397                 return false;
40398             }
40399             return d;
40400         },
40401         
40402         setDialCode : function(v)
40403         {
40404             this.dialCodeHolder.dom.value = '+'+v;
40405         },
40406         
40407         setFlagClass : function(n)
40408         {
40409             this.flag.dom.className = 'flag '+n;
40410         },
40411         
40412         getValue : function()
40413         {
40414             var v = this.inputEl().getValue();
40415             if(this.dialCodeHolder) {
40416                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40417             }
40418             return v;
40419         },
40420         
40421         setValue : function(v)
40422         {
40423             var d = this.getDialCode(v);
40424             
40425             //invalid dial code
40426             if(v.length == 0 || !d || d.length == 0) {
40427                 if(this.rendered){
40428                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40429                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40430                 }
40431                 return;
40432             }
40433             
40434             //valid dial code
40435             this.setFlagClass(this.dialCodeMapping[d].iso2);
40436             this.setDialCode(d);
40437             this.inputEl().dom.value = v.replace('+'+d,'');
40438             this.hiddenEl().dom.value = this.getValue();
40439             
40440             this.validate();
40441         },
40442         
40443         getDialCode : function(v)
40444         {
40445             v = v ||  '';
40446             
40447             if (v.length == 0) {
40448                 return this.dialCodeHolder.dom.value;
40449             }
40450             
40451             var dialCode = "";
40452             if (v.charAt(0) != "+") {
40453                 return false;
40454             }
40455             var numericChars = "";
40456             for (var i = 1; i < v.length; i++) {
40457               var c = v.charAt(i);
40458               if (!isNaN(c)) {
40459                 numericChars += c;
40460                 if (this.dialCodeMapping[numericChars]) {
40461                   dialCode = v.substr(1, i);
40462                 }
40463                 if (numericChars.length == 4) {
40464                   break;
40465                 }
40466               }
40467             }
40468             return dialCode;
40469         },
40470         
40471         reset : function()
40472         {
40473             this.setValue(this.defaultDialCode);
40474             this.validate();
40475         },
40476         
40477         hiddenEl : function()
40478         {
40479             return this.el.select('input.hidden-tel-input',true).first();
40480         },
40481         
40482         // after setting val
40483         onKeyUp : function(e){
40484             this.setValue(this.getValue());
40485         },
40486         
40487         onKeyPress : function(e){
40488             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40489                 e.stopEvent();
40490             }
40491         }
40492         
40493 });
40494 /**
40495  * @class Roo.bootstrap.MoneyField
40496  * @extends Roo.bootstrap.ComboBox
40497  * Bootstrap MoneyField class
40498  * 
40499  * @constructor
40500  * Create a new MoneyField.
40501  * @param {Object} config Configuration options
40502  */
40503
40504 Roo.bootstrap.MoneyField = function(config) {
40505     
40506     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40507     
40508 };
40509
40510 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40511     
40512     /**
40513      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40514      */
40515     allowDecimals : true,
40516     /**
40517      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40518      */
40519     decimalSeparator : ".",
40520     /**
40521      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40522      */
40523     decimalPrecision : 0,
40524     /**
40525      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40526      */
40527     allowNegative : true,
40528     /**
40529      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40530      */
40531     allowZero: true,
40532     /**
40533      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40534      */
40535     minValue : Number.NEGATIVE_INFINITY,
40536     /**
40537      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40538      */
40539     maxValue : Number.MAX_VALUE,
40540     /**
40541      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40542      */
40543     minText : "The minimum value for this field is {0}",
40544     /**
40545      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40546      */
40547     maxText : "The maximum value for this field is {0}",
40548     /**
40549      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40550      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40551      */
40552     nanText : "{0} is not a valid number",
40553     /**
40554      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40555      */
40556     castInt : true,
40557     /**
40558      * @cfg {String} defaults currency of the MoneyField
40559      * value should be in lkey
40560      */
40561     defaultCurrency : false,
40562     /**
40563      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40564      */
40565     thousandsDelimiter : false,
40566     /**
40567      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40568      */
40569     max_length: false,
40570     
40571     inputlg : 9,
40572     inputmd : 9,
40573     inputsm : 9,
40574     inputxs : 6,
40575     
40576     store : false,
40577     
40578     getAutoCreate : function()
40579     {
40580         var align = this.labelAlign || this.parentLabelAlign();
40581         
40582         var id = Roo.id();
40583
40584         var cfg = {
40585             cls: 'form-group',
40586             cn: []
40587         };
40588
40589         var input =  {
40590             tag: 'input',
40591             id : id,
40592             cls : 'form-control roo-money-amount-input',
40593             autocomplete: 'new-password'
40594         };
40595         
40596         var hiddenInput = {
40597             tag: 'input',
40598             type: 'hidden',
40599             id: Roo.id(),
40600             cls: 'hidden-number-input'
40601         };
40602         
40603         if(this.max_length) {
40604             input.maxlength = this.max_length; 
40605         }
40606         
40607         if (this.name) {
40608             hiddenInput.name = this.name;
40609         }
40610
40611         if (this.disabled) {
40612             input.disabled = true;
40613         }
40614
40615         var clg = 12 - this.inputlg;
40616         var cmd = 12 - this.inputmd;
40617         var csm = 12 - this.inputsm;
40618         var cxs = 12 - this.inputxs;
40619         
40620         var container = {
40621             tag : 'div',
40622             cls : 'row roo-money-field',
40623             cn : [
40624                 {
40625                     tag : 'div',
40626                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40627                     cn : [
40628                         {
40629                             tag : 'div',
40630                             cls: 'roo-select2-container input-group',
40631                             cn: [
40632                                 {
40633                                     tag : 'input',
40634                                     cls : 'form-control roo-money-currency-input',
40635                                     autocomplete: 'new-password',
40636                                     readOnly : 1,
40637                                     name : this.currencyName
40638                                 },
40639                                 {
40640                                     tag :'span',
40641                                     cls : 'input-group-addon',
40642                                     cn : [
40643                                         {
40644                                             tag: 'span',
40645                                             cls: 'caret'
40646                                         }
40647                                     ]
40648                                 }
40649                             ]
40650                         }
40651                     ]
40652                 },
40653                 {
40654                     tag : 'div',
40655                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40656                     cn : [
40657                         {
40658                             tag: 'div',
40659                             cls: this.hasFeedback ? 'has-feedback' : '',
40660                             cn: [
40661                                 input
40662                             ]
40663                         }
40664                     ]
40665                 }
40666             ]
40667             
40668         };
40669         
40670         if (this.fieldLabel.length) {
40671             var indicator = {
40672                 tag: 'i',
40673                 tooltip: 'This field is required'
40674             };
40675
40676             var label = {
40677                 tag: 'label',
40678                 'for':  id,
40679                 cls: 'control-label',
40680                 cn: []
40681             };
40682
40683             var label_text = {
40684                 tag: 'span',
40685                 html: this.fieldLabel
40686             };
40687
40688             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40689             label.cn = [
40690                 indicator,
40691                 label_text
40692             ];
40693
40694             if(this.indicatorpos == 'right') {
40695                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40696                 label.cn = [
40697                     label_text,
40698                     indicator
40699                 ];
40700             }
40701
40702             if(align == 'left') {
40703                 container = {
40704                     tag: 'div',
40705                     cn: [
40706                         container
40707                     ]
40708                 };
40709
40710                 if(this.labelWidth > 12){
40711                     label.style = "width: " + this.labelWidth + 'px';
40712                 }
40713                 if(this.labelWidth < 13 && this.labelmd == 0){
40714                     this.labelmd = this.labelWidth;
40715                 }
40716                 if(this.labellg > 0){
40717                     label.cls += ' col-lg-' + this.labellg;
40718                     input.cls += ' col-lg-' + (12 - this.labellg);
40719                 }
40720                 if(this.labelmd > 0){
40721                     label.cls += ' col-md-' + this.labelmd;
40722                     container.cls += ' col-md-' + (12 - this.labelmd);
40723                 }
40724                 if(this.labelsm > 0){
40725                     label.cls += ' col-sm-' + this.labelsm;
40726                     container.cls += ' col-sm-' + (12 - this.labelsm);
40727                 }
40728                 if(this.labelxs > 0){
40729                     label.cls += ' col-xs-' + this.labelxs;
40730                     container.cls += ' col-xs-' + (12 - this.labelxs);
40731                 }
40732             }
40733         }
40734
40735         cfg.cn = [
40736             label,
40737             container,
40738             hiddenInput
40739         ];
40740         
40741         var settings = this;
40742
40743         ['xs','sm','md','lg'].map(function(size){
40744             if (settings[size]) {
40745                 cfg.cls += ' col-' + size + '-' + settings[size];
40746             }
40747         });
40748         
40749         return cfg;
40750     },
40751     
40752     initEvents : function()
40753     {
40754         this.indicator = this.indicatorEl();
40755         
40756         this.initCurrencyEvent();
40757         
40758         this.initNumberEvent();
40759     },
40760     
40761     initCurrencyEvent : function()
40762     {
40763         if (!this.store) {
40764             throw "can not find store for combo";
40765         }
40766         
40767         this.store = Roo.factory(this.store, Roo.data);
40768         this.store.parent = this;
40769         
40770         this.createList();
40771         
40772         this.triggerEl = this.el.select('.input-group-addon', true).first();
40773         
40774         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40775         
40776         var _this = this;
40777         
40778         (function(){
40779             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40780             _this.list.setWidth(lw);
40781         }).defer(100);
40782         
40783         this.list.on('mouseover', this.onViewOver, this);
40784         this.list.on('mousemove', this.onViewMove, this);
40785         this.list.on('scroll', this.onViewScroll, this);
40786         
40787         if(!this.tpl){
40788             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40789         }
40790         
40791         this.view = new Roo.View(this.list, this.tpl, {
40792             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40793         });
40794         
40795         this.view.on('click', this.onViewClick, this);
40796         
40797         this.store.on('beforeload', this.onBeforeLoad, this);
40798         this.store.on('load', this.onLoad, this);
40799         this.store.on('loadexception', this.onLoadException, this);
40800         
40801         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40802             "up" : function(e){
40803                 this.inKeyMode = true;
40804                 this.selectPrev();
40805             },
40806
40807             "down" : function(e){
40808                 if(!this.isExpanded()){
40809                     this.onTriggerClick();
40810                 }else{
40811                     this.inKeyMode = true;
40812                     this.selectNext();
40813                 }
40814             },
40815
40816             "enter" : function(e){
40817                 this.collapse();
40818                 
40819                 if(this.fireEvent("specialkey", this, e)){
40820                     this.onViewClick(false);
40821                 }
40822                 
40823                 return true;
40824             },
40825
40826             "esc" : function(e){
40827                 this.collapse();
40828             },
40829
40830             "tab" : function(e){
40831                 this.collapse();
40832                 
40833                 if(this.fireEvent("specialkey", this, e)){
40834                     this.onViewClick(false);
40835                 }
40836                 
40837                 return true;
40838             },
40839
40840             scope : this,
40841
40842             doRelay : function(foo, bar, hname){
40843                 if(hname == 'down' || this.scope.isExpanded()){
40844                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40845                 }
40846                 return true;
40847             },
40848
40849             forceKeyDown: true
40850         });
40851         
40852         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40853         
40854     },
40855     
40856     initNumberEvent : function(e)
40857     {
40858         this.inputEl().on("keydown" , this.fireKey,  this);
40859         this.inputEl().on("focus", this.onFocus,  this);
40860         this.inputEl().on("blur", this.onBlur,  this);
40861         
40862         this.inputEl().relayEvent('keyup', this);
40863         
40864         if(this.indicator){
40865             this.indicator.addClass('invisible');
40866         }
40867  
40868         this.originalValue = this.getValue();
40869         
40870         if(this.validationEvent == 'keyup'){
40871             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40872             this.inputEl().on('keyup', this.filterValidation, this);
40873         }
40874         else if(this.validationEvent !== false){
40875             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40876         }
40877         
40878         if(this.selectOnFocus){
40879             this.on("focus", this.preFocus, this);
40880             
40881         }
40882         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40883             this.inputEl().on("keypress", this.filterKeys, this);
40884         } else {
40885             this.inputEl().relayEvent('keypress', this);
40886         }
40887         
40888         var allowed = "0123456789";
40889         
40890         if(this.allowDecimals){
40891             allowed += this.decimalSeparator;
40892         }
40893         
40894         if(this.allowNegative){
40895             allowed += "-";
40896         }
40897         
40898         if(this.thousandsDelimiter) {
40899             allowed += ",";
40900         }
40901         
40902         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40903         
40904         var keyPress = function(e){
40905             
40906             var k = e.getKey();
40907             
40908             var c = e.getCharCode();
40909             
40910             if(
40911                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40912                     allowed.indexOf(String.fromCharCode(c)) === -1
40913             ){
40914                 e.stopEvent();
40915                 return;
40916             }
40917             
40918             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40919                 return;
40920             }
40921             
40922             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40923                 e.stopEvent();
40924             }
40925         };
40926         
40927         this.inputEl().on("keypress", keyPress, this);
40928         
40929     },
40930     
40931     onTriggerClick : function(e)
40932     {   
40933         if(this.disabled){
40934             return;
40935         }
40936         
40937         this.page = 0;
40938         this.loadNext = false;
40939         
40940         if(this.isExpanded()){
40941             this.collapse();
40942             return;
40943         }
40944         
40945         this.hasFocus = true;
40946         
40947         if(this.triggerAction == 'all') {
40948             this.doQuery(this.allQuery, true);
40949             return;
40950         }
40951         
40952         this.doQuery(this.getRawValue());
40953     },
40954     
40955     getCurrency : function()
40956     {   
40957         var v = this.currencyEl().getValue();
40958         
40959         return v;
40960     },
40961     
40962     restrictHeight : function()
40963     {
40964         this.list.alignTo(this.currencyEl(), this.listAlign);
40965         this.list.alignTo(this.currencyEl(), this.listAlign);
40966     },
40967     
40968     onViewClick : function(view, doFocus, el, e)
40969     {
40970         var index = this.view.getSelectedIndexes()[0];
40971         
40972         var r = this.store.getAt(index);
40973         
40974         if(r){
40975             this.onSelect(r, index);
40976         }
40977     },
40978     
40979     onSelect : function(record, index){
40980         
40981         if(this.fireEvent('beforeselect', this, record, index) !== false){
40982         
40983             this.setFromCurrencyData(index > -1 ? record.data : false);
40984             
40985             this.collapse();
40986             
40987             this.fireEvent('select', this, record, index);
40988         }
40989     },
40990     
40991     setFromCurrencyData : function(o)
40992     {
40993         var currency = '';
40994         
40995         this.lastCurrency = o;
40996         
40997         if (this.currencyField) {
40998             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40999         } else {
41000             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41001         }
41002         
41003         this.lastSelectionText = currency;
41004         
41005         //setting default currency
41006         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41007             this.setCurrency(this.defaultCurrency);
41008             return;
41009         }
41010         
41011         this.setCurrency(currency);
41012     },
41013     
41014     setFromData : function(o)
41015     {
41016         var c = {};
41017         
41018         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41019         
41020         this.setFromCurrencyData(c);
41021         
41022         var value = '';
41023         
41024         if (this.name) {
41025             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41026         } else {
41027             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41028         }
41029         
41030         this.setValue(value);
41031         
41032     },
41033     
41034     setCurrency : function(v)
41035     {   
41036         this.currencyValue = v;
41037         
41038         if(this.rendered){
41039             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41040             this.validate();
41041         }
41042     },
41043     
41044     setValue : function(v)
41045     {
41046         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41047         
41048         this.value = v;
41049         
41050         if(this.rendered){
41051             
41052             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41053             
41054             this.inputEl().dom.value = (v == '') ? '' :
41055                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41056             
41057             if(!this.allowZero && v === '0') {
41058                 this.hiddenEl().dom.value = '';
41059                 this.inputEl().dom.value = '';
41060             }
41061             
41062             this.validate();
41063         }
41064     },
41065     
41066     getRawValue : function()
41067     {
41068         var v = this.inputEl().getValue();
41069         
41070         return v;
41071     },
41072     
41073     getValue : function()
41074     {
41075         return this.fixPrecision(this.parseValue(this.getRawValue()));
41076     },
41077     
41078     parseValue : function(value)
41079     {
41080         if(this.thousandsDelimiter) {
41081             value += "";
41082             r = new RegExp(",", "g");
41083             value = value.replace(r, "");
41084         }
41085         
41086         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41087         return isNaN(value) ? '' : value;
41088         
41089     },
41090     
41091     fixPrecision : function(value)
41092     {
41093         if(this.thousandsDelimiter) {
41094             value += "";
41095             r = new RegExp(",", "g");
41096             value = value.replace(r, "");
41097         }
41098         
41099         var nan = isNaN(value);
41100         
41101         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41102             return nan ? '' : value;
41103         }
41104         return parseFloat(value).toFixed(this.decimalPrecision);
41105     },
41106     
41107     decimalPrecisionFcn : function(v)
41108     {
41109         return Math.floor(v);
41110     },
41111     
41112     validateValue : function(value)
41113     {
41114         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41115             return false;
41116         }
41117         
41118         var num = this.parseValue(value);
41119         
41120         if(isNaN(num)){
41121             this.markInvalid(String.format(this.nanText, value));
41122             return false;
41123         }
41124         
41125         if(num < this.minValue){
41126             this.markInvalid(String.format(this.minText, this.minValue));
41127             return false;
41128         }
41129         
41130         if(num > this.maxValue){
41131             this.markInvalid(String.format(this.maxText, this.maxValue));
41132             return false;
41133         }
41134         
41135         return true;
41136     },
41137     
41138     validate : function()
41139     {
41140         if(this.disabled || this.allowBlank){
41141             this.markValid();
41142             return true;
41143         }
41144         
41145         var currency = this.getCurrency();
41146         
41147         if(this.validateValue(this.getRawValue()) && currency.length){
41148             this.markValid();
41149             return true;
41150         }
41151         
41152         this.markInvalid();
41153         return false;
41154     },
41155     
41156     getName: function()
41157     {
41158         return this.name;
41159     },
41160     
41161     beforeBlur : function()
41162     {
41163         if(!this.castInt){
41164             return;
41165         }
41166         
41167         var v = this.parseValue(this.getRawValue());
41168         
41169         if(v || v == 0){
41170             this.setValue(v);
41171         }
41172     },
41173     
41174     onBlur : function()
41175     {
41176         this.beforeBlur();
41177         
41178         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41179             //this.el.removeClass(this.focusClass);
41180         }
41181         
41182         this.hasFocus = false;
41183         
41184         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41185             this.validate();
41186         }
41187         
41188         var v = this.getValue();
41189         
41190         if(String(v) !== String(this.startValue)){
41191             this.fireEvent('change', this, v, this.startValue);
41192         }
41193         
41194         this.fireEvent("blur", this);
41195     },
41196     
41197     inputEl : function()
41198     {
41199         return this.el.select('.roo-money-amount-input', true).first();
41200     },
41201     
41202     currencyEl : function()
41203     {
41204         return this.el.select('.roo-money-currency-input', true).first();
41205     },
41206     
41207     hiddenEl : function()
41208     {
41209         return this.el.select('input.hidden-number-input',true).first();
41210     }
41211     
41212 });