Roo/bootstrap/NavItem.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.push({
4142                 tag: 'a',
4143                 href: this.brand_href ? this.brand_href : '#',
4144                 cls: 'navbar-brand',
4145                 cn: [
4146                 this.brand
4147                 ]
4148             });
4149         }
4150         
4151         if(this.main){
4152             cfg.cls += ' main-nav';
4153         }
4154         
4155         
4156         return cfg;
4157
4158         
4159     },
4160     getHeaderChildContainer : function()
4161     {
4162         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4163             return this.el.select('.navbar-header',true).first();
4164         }
4165         
4166         return this.getChildContainer();
4167     },
4168     
4169     
4170     initEvents : function()
4171     {
4172         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4173         
4174         if (this.autohide) {
4175             
4176             var prevScroll = 0;
4177             var ft = this.el;
4178             
4179             Roo.get(document).on('scroll',function(e) {
4180                 var ns = Roo.get(document).getScroll().top;
4181                 var os = prevScroll;
4182                 prevScroll = ns;
4183                 
4184                 if(ns > os){
4185                     ft.removeClass('slideDown');
4186                     ft.addClass('slideUp');
4187                     return;
4188                 }
4189                 ft.removeClass('slideUp');
4190                 ft.addClass('slideDown');
4191                  
4192               
4193           },this);
4194         }
4195     }    
4196     
4197 });
4198
4199
4200
4201  
4202
4203  /*
4204  * - LGPL
4205  *
4206  * navbar
4207  * 
4208  */
4209
4210 /**
4211  * @class Roo.bootstrap.NavSidebar
4212  * @extends Roo.bootstrap.Navbar
4213  * Bootstrap Sidebar class
4214  * 
4215  * @constructor
4216  * Create a new Sidebar
4217  * @param {Object} config The config object
4218  */
4219
4220
4221 Roo.bootstrap.NavSidebar = function(config){
4222     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4223 };
4224
4225 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4226     
4227     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4228     
4229     getAutoCreate : function(){
4230         
4231         
4232         return  {
4233             tag: 'div',
4234             cls: 'sidebar sidebar-nav'
4235         };
4236     
4237         
4238     }
4239     
4240     
4241     
4242 });
4243
4244
4245
4246  
4247
4248  /*
4249  * - LGPL
4250  *
4251  * nav group
4252  * 
4253  */
4254
4255 /**
4256  * @class Roo.bootstrap.NavGroup
4257  * @extends Roo.bootstrap.Component
4258  * Bootstrap NavGroup class
4259  * @cfg {String} align (left|right)
4260  * @cfg {Boolean} inverse
4261  * @cfg {String} type (nav|pills|tab) default nav
4262  * @cfg {String} navId - reference Id for navbar.
4263
4264  * 
4265  * @constructor
4266  * Create a new nav group
4267  * @param {Object} config The config object
4268  */
4269
4270 Roo.bootstrap.NavGroup = function(config){
4271     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4272     this.navItems = [];
4273    
4274     Roo.bootstrap.NavGroup.register(this);
4275      this.addEvents({
4276         /**
4277              * @event changed
4278              * Fires when the active item changes
4279              * @param {Roo.bootstrap.NavGroup} this
4280              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4281              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4282          */
4283         'changed': true
4284      });
4285     
4286 };
4287
4288 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4289     
4290     align: '',
4291     inverse: false,
4292     form: false,
4293     type: 'nav',
4294     navId : '',
4295     // private
4296     
4297     navItems : false, 
4298     
4299     getAutoCreate : function()
4300     {
4301         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4302         
4303         cfg = {
4304             tag : 'ul',
4305             cls: 'nav' 
4306         };
4307         
4308         if (['tabs','pills'].indexOf(this.type)!==-1) {
4309             cfg.cls += ' nav-' + this.type
4310         } else {
4311             if (this.type!=='nav') {
4312                 Roo.log('nav type must be nav/tabs/pills')
4313             }
4314             cfg.cls += ' navbar-nav'
4315         }
4316         
4317         if (this.parent() && this.parent().sidebar) {
4318             cfg = {
4319                 tag: 'ul',
4320                 cls: 'dashboard-menu sidebar-menu'
4321             };
4322             
4323             return cfg;
4324         }
4325         
4326         if (this.form === true) {
4327             cfg = {
4328                 tag: 'form',
4329                 cls: 'navbar-form'
4330             };
4331             
4332             if (this.align === 'right') {
4333                 cfg.cls += ' navbar-right ml-md-auto';
4334             } else {
4335                 cfg.cls += ' navbar-left';
4336             }
4337         }
4338         
4339         if (this.align === 'right') {
4340             cfg.cls += ' navbar-right ml-md-auto';
4341         } else {
4342             cfg.cls += ' mr-auto';
4343         }
4344         
4345         if (this.inverse) {
4346             cfg.cls += ' navbar-inverse';
4347             
4348         }
4349         
4350         
4351         return cfg;
4352     },
4353     /**
4354     * sets the active Navigation item
4355     * @param {Roo.bootstrap.NavItem} the new current navitem
4356     */
4357     setActiveItem : function(item)
4358     {
4359         var prev = false;
4360         Roo.each(this.navItems, function(v){
4361             if (v == item) {
4362                 return ;
4363             }
4364             if (v.isActive()) {
4365                 v.setActive(false, true);
4366                 prev = v;
4367                 
4368             }
4369             
4370         });
4371
4372         item.setActive(true, true);
4373         this.fireEvent('changed', this, item, prev);
4374         
4375         
4376     },
4377     /**
4378     * gets the active Navigation item
4379     * @return {Roo.bootstrap.NavItem} the current navitem
4380     */
4381     getActive : function()
4382     {
4383         
4384         var prev = false;
4385         Roo.each(this.navItems, function(v){
4386             
4387             if (v.isActive()) {
4388                 prev = v;
4389                 
4390             }
4391             
4392         });
4393         return prev;
4394     },
4395     
4396     indexOfNav : function()
4397     {
4398         
4399         var prev = false;
4400         Roo.each(this.navItems, function(v,i){
4401             
4402             if (v.isActive()) {
4403                 prev = i;
4404                 
4405             }
4406             
4407         });
4408         return prev;
4409     },
4410     /**
4411     * adds a Navigation item
4412     * @param {Roo.bootstrap.NavItem} the navitem to add
4413     */
4414     addItem : function(cfg)
4415     {
4416         var cn = new Roo.bootstrap.NavItem(cfg);
4417         this.register(cn);
4418         cn.parentId = this.id;
4419         cn.onRender(this.el, null);
4420         return cn;
4421     },
4422     /**
4423     * register a Navigation item
4424     * @param {Roo.bootstrap.NavItem} the navitem to add
4425     */
4426     register : function(item)
4427     {
4428         this.navItems.push( item);
4429         item.navId = this.navId;
4430     
4431     },
4432     
4433     /**
4434     * clear all the Navigation item
4435     */
4436    
4437     clearAll : function()
4438     {
4439         this.navItems = [];
4440         this.el.dom.innerHTML = '';
4441     },
4442     
4443     getNavItem: function(tabId)
4444     {
4445         var ret = false;
4446         Roo.each(this.navItems, function(e) {
4447             if (e.tabId == tabId) {
4448                ret =  e;
4449                return false;
4450             }
4451             return true;
4452             
4453         });
4454         return ret;
4455     },
4456     
4457     setActiveNext : function()
4458     {
4459         var i = this.indexOfNav(this.getActive());
4460         if (i > this.navItems.length) {
4461             return;
4462         }
4463         this.setActiveItem(this.navItems[i+1]);
4464     },
4465     setActivePrev : function()
4466     {
4467         var i = this.indexOfNav(this.getActive());
4468         if (i  < 1) {
4469             return;
4470         }
4471         this.setActiveItem(this.navItems[i-1]);
4472     },
4473     clearWasActive : function(except) {
4474         Roo.each(this.navItems, function(e) {
4475             if (e.tabId != except.tabId && e.was_active) {
4476                e.was_active = false;
4477                return false;
4478             }
4479             return true;
4480             
4481         });
4482     },
4483     getWasActive : function ()
4484     {
4485         var r = false;
4486         Roo.each(this.navItems, function(e) {
4487             if (e.was_active) {
4488                r = e;
4489                return false;
4490             }
4491             return true;
4492             
4493         });
4494         return r;
4495     }
4496     
4497     
4498 });
4499
4500  
4501 Roo.apply(Roo.bootstrap.NavGroup, {
4502     
4503     groups: {},
4504      /**
4505     * register a Navigation Group
4506     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4507     */
4508     register : function(navgrp)
4509     {
4510         this.groups[navgrp.navId] = navgrp;
4511         
4512     },
4513     /**
4514     * fetch a Navigation Group based on the navigation ID
4515     * @param {string} the navgroup to add
4516     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4517     */
4518     get: function(navId) {
4519         if (typeof(this.groups[navId]) == 'undefined') {
4520             return false;
4521             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4522         }
4523         return this.groups[navId] ;
4524     }
4525     
4526     
4527     
4528 });
4529
4530  /*
4531  * - LGPL
4532  *
4533  * row
4534  * 
4535  */
4536
4537 /**
4538  * @class Roo.bootstrap.NavItem
4539  * @extends Roo.bootstrap.Component
4540  * Bootstrap Navbar.NavItem class
4541  * @cfg {String} href  link to
4542  * @cfg {String} html content of button
4543  * @cfg {String} badge text inside badge
4544  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4545  * @cfg {String} glyphicon DEPRICATED - use fa
4546  * @cfg {String} icon DEPRICATED - use fa
4547  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4548  * @cfg {Boolean} active Is item active
4549  * @cfg {Boolean} disabled Is item disabled
4550  
4551  * @cfg {Boolean} preventDefault (true | false) default false
4552  * @cfg {String} tabId the tab that this item activates.
4553  * @cfg {String} tagtype (a|span) render as a href or span?
4554  * @cfg {Boolean} animateRef (true|false) link to element default false  
4555   
4556  * @constructor
4557  * Create a new Navbar Item
4558  * @param {Object} config The config object
4559  */
4560 Roo.bootstrap.NavItem = function(config){
4561     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4562     this.addEvents({
4563         // raw events
4564         /**
4565          * @event click
4566          * The raw click event for the entire grid.
4567          * @param {Roo.EventObject} e
4568          */
4569         "click" : true,
4570          /**
4571             * @event changed
4572             * Fires when the active item active state changes
4573             * @param {Roo.bootstrap.NavItem} this
4574             * @param {boolean} state the new state
4575              
4576          */
4577         'changed': true,
4578         /**
4579             * @event scrollto
4580             * Fires when scroll to element
4581             * @param {Roo.bootstrap.NavItem} this
4582             * @param {Object} options
4583             * @param {Roo.EventObject} e
4584              
4585          */
4586         'scrollto': true
4587     });
4588    
4589 };
4590
4591 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4592     
4593     href: false,
4594     html: '',
4595     badge: '',
4596     icon: false,
4597     fa : false,
4598     glyphicon: false,
4599     active: false,
4600     preventDefault : false,
4601     tabId : false,
4602     tagtype : 'a',
4603     disabled : false,
4604     animateRef : false,
4605     was_active : false,
4606     
4607     getAutoCreate : function(){
4608          
4609         var cfg = {
4610             tag: 'li',
4611             cls: 'nav-item'
4612             
4613         };
4614         
4615         if (this.active) {
4616             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4617         }
4618         if (this.disabled) {
4619             cfg.cls += ' disabled';
4620         }
4621         
4622         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4623             cfg.cn = [
4624                 {
4625                     tag: this.tagtype,
4626                     href : this.href || "#",
4627                     html: this.html || ''
4628                 }
4629             ];
4630             if (this.tagtype == 'a') {
4631                 cfg.cn[0].cls = 'nav-link';
4632             }
4633             if (this.icon) {
4634                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4635             }
4636             if (this.fa) {
4637                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4638             }
4639             if(this.glyphicon) {
4640                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4641             }
4642             
4643             if (this.menu) {
4644                 
4645                 cfg.cn[0].html += " <span class='caret'></span>";
4646              
4647             }
4648             
4649             if (this.badge !== '') {
4650                  
4651                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4652             }
4653         }
4654         
4655         
4656         
4657         return cfg;
4658     },
4659     initEvents: function() 
4660     {
4661         if (typeof (this.menu) != 'undefined') {
4662             this.menu.parentType = this.xtype;
4663             this.menu.triggerEl = this.el;
4664             this.menu = this.addxtype(Roo.apply({}, this.menu));
4665         }
4666         
4667         this.el.select('a',true).on('click', this.onClick, this);
4668         
4669         if(this.tagtype == 'span'){
4670             this.el.select('span',true).on('click', this.onClick, this);
4671         }
4672        
4673         // at this point parent should be available..
4674         this.parent().register(this);
4675     },
4676     
4677     onClick : function(e)
4678     {
4679         if (e.getTarget('.dropdown-menu-item')) {
4680             // did you click on a menu itemm.... - then don't trigger onclick..
4681             return;
4682         }
4683         
4684         if(
4685                 this.preventDefault || 
4686                 this.href == '#' 
4687         ){
4688             Roo.log("NavItem - prevent Default?");
4689             e.preventDefault();
4690         }
4691         
4692         if (this.disabled) {
4693             return;
4694         }
4695         
4696         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4697         if (tg && tg.transition) {
4698             Roo.log("waiting for the transitionend");
4699             return;
4700         }
4701         
4702         
4703         
4704         //Roo.log("fire event clicked");
4705         if(this.fireEvent('click', this, e) === false){
4706             return;
4707         };
4708         
4709         if(this.tagtype == 'span'){
4710             return;
4711         }
4712         
4713         //Roo.log(this.href);
4714         var ael = this.el.select('a',true).first();
4715         //Roo.log(ael);
4716         
4717         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4718             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4719             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4720                 return; // ignore... - it's a 'hash' to another page.
4721             }
4722             Roo.log("NavItem - prevent Default?");
4723             e.preventDefault();
4724             this.scrollToElement(e);
4725         }
4726         
4727         
4728         var p =  this.parent();
4729    
4730         if (['tabs','pills'].indexOf(p.type)!==-1) {
4731             if (typeof(p.setActiveItem) !== 'undefined') {
4732                 p.setActiveItem(this);
4733             }
4734         }
4735         
4736         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4737         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4738             // remove the collapsed menu expand...
4739             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4740         }
4741     },
4742     
4743     isActive: function () {
4744         return this.active
4745     },
4746     setActive : function(state, fire, is_was_active)
4747     {
4748         if (this.active && !state && this.navId) {
4749             this.was_active = true;
4750             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4751             if (nv) {
4752                 nv.clearWasActive(this);
4753             }
4754             
4755         }
4756         this.active = state;
4757         
4758         if (!state ) {
4759             this.el.removeClass('active');
4760         } else if (!this.el.hasClass('active')) {
4761             this.el.addClass('active');
4762         }
4763         if (fire) {
4764             this.fireEvent('changed', this, state);
4765         }
4766         
4767         // show a panel if it's registered and related..
4768         
4769         if (!this.navId || !this.tabId || !state || is_was_active) {
4770             return;
4771         }
4772         
4773         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4774         if (!tg) {
4775             return;
4776         }
4777         var pan = tg.getPanelByName(this.tabId);
4778         if (!pan) {
4779             return;
4780         }
4781         // if we can not flip to new panel - go back to old nav highlight..
4782         if (false == tg.showPanel(pan)) {
4783             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4784             if (nv) {
4785                 var onav = nv.getWasActive();
4786                 if (onav) {
4787                     onav.setActive(true, false, true);
4788                 }
4789             }
4790             
4791         }
4792         
4793         
4794         
4795     },
4796      // this should not be here...
4797     setDisabled : function(state)
4798     {
4799         this.disabled = state;
4800         if (!state ) {
4801             this.el.removeClass('disabled');
4802         } else if (!this.el.hasClass('disabled')) {
4803             this.el.addClass('disabled');
4804         }
4805         
4806     },
4807     
4808     /**
4809      * Fetch the element to display the tooltip on.
4810      * @return {Roo.Element} defaults to this.el
4811      */
4812     tooltipEl : function()
4813     {
4814         return this.el.select('' + this.tagtype + '', true).first();
4815     },
4816     
4817     scrollToElement : function(e)
4818     {
4819         var c = document.body;
4820         
4821         /*
4822          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4823          */
4824         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4825             c = document.documentElement;
4826         }
4827         
4828         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4829         
4830         if(!target){
4831             return;
4832         }
4833
4834         var o = target.calcOffsetsTo(c);
4835         
4836         var options = {
4837             target : target,
4838             value : o[1]
4839         };
4840         
4841         this.fireEvent('scrollto', this, options, e);
4842         
4843         Roo.get(c).scrollTo('top', options.value, true);
4844         
4845         return;
4846     }
4847 });
4848  
4849
4850  /*
4851  * - LGPL
4852  *
4853  * sidebar item
4854  *
4855  *  li
4856  *    <span> icon </span>
4857  *    <span> text </span>
4858  *    <span>badge </span>
4859  */
4860
4861 /**
4862  * @class Roo.bootstrap.NavSidebarItem
4863  * @extends Roo.bootstrap.NavItem
4864  * Bootstrap Navbar.NavSidebarItem class
4865  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4866  * {Boolean} open is the menu open
4867  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4868  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4869  * {String} buttonSize (sm|md|lg)the extra classes for the button
4870  * {Boolean} showArrow show arrow next to the text (default true)
4871  * @constructor
4872  * Create a new Navbar Button
4873  * @param {Object} config The config object
4874  */
4875 Roo.bootstrap.NavSidebarItem = function(config){
4876     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4877     this.addEvents({
4878         // raw events
4879         /**
4880          * @event click
4881          * The raw click event for the entire grid.
4882          * @param {Roo.EventObject} e
4883          */
4884         "click" : true,
4885          /**
4886             * @event changed
4887             * Fires when the active item active state changes
4888             * @param {Roo.bootstrap.NavSidebarItem} this
4889             * @param {boolean} state the new state
4890              
4891          */
4892         'changed': true
4893     });
4894    
4895 };
4896
4897 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4898     
4899     badgeWeight : 'default',
4900     
4901     open: false,
4902     
4903     buttonView : false,
4904     
4905     buttonWeight : 'default',
4906     
4907     buttonSize : 'md',
4908     
4909     showArrow : true,
4910     
4911     getAutoCreate : function(){
4912         
4913         
4914         var a = {
4915                 tag: 'a',
4916                 href : this.href || '#',
4917                 cls: '',
4918                 html : '',
4919                 cn : []
4920         };
4921         
4922         if(this.buttonView){
4923             a = {
4924                 tag: 'button',
4925                 href : this.href || '#',
4926                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4927                 html : this.html,
4928                 cn : []
4929             };
4930         }
4931         
4932         var cfg = {
4933             tag: 'li',
4934             cls: '',
4935             cn: [ a ]
4936         };
4937         
4938         if (this.active) {
4939             cfg.cls += ' active';
4940         }
4941         
4942         if (this.disabled) {
4943             cfg.cls += ' disabled';
4944         }
4945         if (this.open) {
4946             cfg.cls += ' open x-open';
4947         }
4948         // left icon..
4949         if (this.glyphicon || this.icon) {
4950             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4951             a.cn.push({ tag : 'i', cls : c }) ;
4952         }
4953         
4954         if(!this.buttonView){
4955             var span = {
4956                 tag: 'span',
4957                 html : this.html || ''
4958             };
4959
4960             a.cn.push(span);
4961             
4962         }
4963         
4964         if (this.badge !== '') {
4965             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4966         }
4967         
4968         if (this.menu) {
4969             
4970             if(this.showArrow){
4971                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4972             }
4973             
4974             a.cls += ' dropdown-toggle treeview' ;
4975         }
4976         
4977         return cfg;
4978     },
4979     
4980     initEvents : function()
4981     { 
4982         if (typeof (this.menu) != 'undefined') {
4983             this.menu.parentType = this.xtype;
4984             this.menu.triggerEl = this.el;
4985             this.menu = this.addxtype(Roo.apply({}, this.menu));
4986         }
4987         
4988         this.el.on('click', this.onClick, this);
4989         
4990         if(this.badge !== ''){
4991             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4992         }
4993         
4994     },
4995     
4996     onClick : function(e)
4997     {
4998         if(this.disabled){
4999             e.preventDefault();
5000             return;
5001         }
5002         
5003         if(this.preventDefault){
5004             e.preventDefault();
5005         }
5006         
5007         this.fireEvent('click', this);
5008     },
5009     
5010     disable : function()
5011     {
5012         this.setDisabled(true);
5013     },
5014     
5015     enable : function()
5016     {
5017         this.setDisabled(false);
5018     },
5019     
5020     setDisabled : function(state)
5021     {
5022         if(this.disabled == state){
5023             return;
5024         }
5025         
5026         this.disabled = state;
5027         
5028         if (state) {
5029             this.el.addClass('disabled');
5030             return;
5031         }
5032         
5033         this.el.removeClass('disabled');
5034         
5035         return;
5036     },
5037     
5038     setActive : function(state)
5039     {
5040         if(this.active == state){
5041             return;
5042         }
5043         
5044         this.active = state;
5045         
5046         if (state) {
5047             this.el.addClass('active');
5048             return;
5049         }
5050         
5051         this.el.removeClass('active');
5052         
5053         return;
5054     },
5055     
5056     isActive: function () 
5057     {
5058         return this.active;
5059     },
5060     
5061     setBadge : function(str)
5062     {
5063         if(!this.badgeEl){
5064             return;
5065         }
5066         
5067         this.badgeEl.dom.innerHTML = str;
5068     }
5069     
5070    
5071      
5072  
5073 });
5074  
5075
5076  /*
5077  * - LGPL
5078  *
5079  * row
5080  * 
5081  */
5082
5083 /**
5084  * @class Roo.bootstrap.Row
5085  * @extends Roo.bootstrap.Component
5086  * Bootstrap Row class (contains columns...)
5087  * 
5088  * @constructor
5089  * Create a new Row
5090  * @param {Object} config The config object
5091  */
5092
5093 Roo.bootstrap.Row = function(config){
5094     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5095 };
5096
5097 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5098     
5099     getAutoCreate : function(){
5100        return {
5101             cls: 'row clearfix'
5102        };
5103     }
5104     
5105     
5106 });
5107
5108  
5109
5110  /*
5111  * - LGPL
5112  *
5113  * element
5114  * 
5115  */
5116
5117 /**
5118  * @class Roo.bootstrap.Element
5119  * @extends Roo.bootstrap.Component
5120  * Bootstrap Element class
5121  * @cfg {String} html contents of the element
5122  * @cfg {String} tag tag of the element
5123  * @cfg {String} cls class of the element
5124  * @cfg {Boolean} preventDefault (true|false) default false
5125  * @cfg {Boolean} clickable (true|false) default false
5126  * 
5127  * @constructor
5128  * Create a new Element
5129  * @param {Object} config The config object
5130  */
5131
5132 Roo.bootstrap.Element = function(config){
5133     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5134     
5135     this.addEvents({
5136         // raw events
5137         /**
5138          * @event click
5139          * When a element is chick
5140          * @param {Roo.bootstrap.Element} this
5141          * @param {Roo.EventObject} e
5142          */
5143         "click" : true
5144     });
5145 };
5146
5147 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5148     
5149     tag: 'div',
5150     cls: '',
5151     html: '',
5152     preventDefault: false, 
5153     clickable: false,
5154     
5155     getAutoCreate : function(){
5156         
5157         var cfg = {
5158             tag: this.tag,
5159             // cls: this.cls, double assign in parent class Component.js :: onRender
5160             html: this.html
5161         };
5162         
5163         return cfg;
5164     },
5165     
5166     initEvents: function() 
5167     {
5168         Roo.bootstrap.Element.superclass.initEvents.call(this);
5169         
5170         if(this.clickable){
5171             this.el.on('click', this.onClick, this);
5172         }
5173         
5174     },
5175     
5176     onClick : function(e)
5177     {
5178         if(this.preventDefault){
5179             e.preventDefault();
5180         }
5181         
5182         this.fireEvent('click', this, e);
5183     },
5184     
5185     getValue : function()
5186     {
5187         return this.el.dom.innerHTML;
5188     },
5189     
5190     setValue : function(value)
5191     {
5192         this.el.dom.innerHTML = value;
5193     }
5194    
5195 });
5196
5197  
5198
5199  /*
5200  * - LGPL
5201  *
5202  * pagination
5203  * 
5204  */
5205
5206 /**
5207  * @class Roo.bootstrap.Pagination
5208  * @extends Roo.bootstrap.Component
5209  * Bootstrap Pagination class
5210  * @cfg {String} size xs | sm | md | lg
5211  * @cfg {Boolean} inverse false | true
5212  * 
5213  * @constructor
5214  * Create a new Pagination
5215  * @param {Object} config The config object
5216  */
5217
5218 Roo.bootstrap.Pagination = function(config){
5219     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5220 };
5221
5222 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5223     
5224     cls: false,
5225     size: false,
5226     inverse: false,
5227     
5228     getAutoCreate : function(){
5229         var cfg = {
5230             tag: 'ul',
5231                 cls: 'pagination'
5232         };
5233         if (this.inverse) {
5234             cfg.cls += ' inverse';
5235         }
5236         if (this.html) {
5237             cfg.html=this.html;
5238         }
5239         if (this.cls) {
5240             cfg.cls += " " + this.cls;
5241         }
5242         return cfg;
5243     }
5244    
5245 });
5246
5247  
5248
5249  /*
5250  * - LGPL
5251  *
5252  * Pagination item
5253  * 
5254  */
5255
5256
5257 /**
5258  * @class Roo.bootstrap.PaginationItem
5259  * @extends Roo.bootstrap.Component
5260  * Bootstrap PaginationItem class
5261  * @cfg {String} html text
5262  * @cfg {String} href the link
5263  * @cfg {Boolean} preventDefault (true | false) default true
5264  * @cfg {Boolean} active (true | false) default false
5265  * @cfg {Boolean} disabled default false
5266  * 
5267  * 
5268  * @constructor
5269  * Create a new PaginationItem
5270  * @param {Object} config The config object
5271  */
5272
5273
5274 Roo.bootstrap.PaginationItem = function(config){
5275     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5276     this.addEvents({
5277         // raw events
5278         /**
5279          * @event click
5280          * The raw click event for the entire grid.
5281          * @param {Roo.EventObject} e
5282          */
5283         "click" : true
5284     });
5285 };
5286
5287 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5288     
5289     href : false,
5290     html : false,
5291     preventDefault: true,
5292     active : false,
5293     cls : false,
5294     disabled: false,
5295     
5296     getAutoCreate : function(){
5297         var cfg= {
5298             tag: 'li',
5299             cn: [
5300                 {
5301                     tag : 'a',
5302                     href : this.href ? this.href : '#',
5303                     html : this.html ? this.html : ''
5304                 }
5305             ]
5306         };
5307         
5308         if(this.cls){
5309             cfg.cls = this.cls;
5310         }
5311         
5312         if(this.disabled){
5313             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5314         }
5315         
5316         if(this.active){
5317             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5318         }
5319         
5320         return cfg;
5321     },
5322     
5323     initEvents: function() {
5324         
5325         this.el.on('click', this.onClick, this);
5326         
5327     },
5328     onClick : function(e)
5329     {
5330         Roo.log('PaginationItem on click ');
5331         if(this.preventDefault){
5332             e.preventDefault();
5333         }
5334         
5335         if(this.disabled){
5336             return;
5337         }
5338         
5339         this.fireEvent('click', this, e);
5340     }
5341    
5342 });
5343
5344  
5345
5346  /*
5347  * - LGPL
5348  *
5349  * slider
5350  * 
5351  */
5352
5353
5354 /**
5355  * @class Roo.bootstrap.Slider
5356  * @extends Roo.bootstrap.Component
5357  * Bootstrap Slider class
5358  *    
5359  * @constructor
5360  * Create a new Slider
5361  * @param {Object} config The config object
5362  */
5363
5364 Roo.bootstrap.Slider = function(config){
5365     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5366 };
5367
5368 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5369     
5370     getAutoCreate : function(){
5371         
5372         var cfg = {
5373             tag: 'div',
5374             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5375             cn: [
5376                 {
5377                     tag: 'a',
5378                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5379                 }
5380             ]
5381         };
5382         
5383         return cfg;
5384     }
5385    
5386 });
5387
5388  /*
5389  * Based on:
5390  * Ext JS Library 1.1.1
5391  * Copyright(c) 2006-2007, Ext JS, LLC.
5392  *
5393  * Originally Released Under LGPL - original licence link has changed is not relivant.
5394  *
5395  * Fork - LGPL
5396  * <script type="text/javascript">
5397  */
5398  
5399
5400 /**
5401  * @class Roo.grid.ColumnModel
5402  * @extends Roo.util.Observable
5403  * This is the default implementation of a ColumnModel used by the Grid. It defines
5404  * the columns in the grid.
5405  * <br>Usage:<br>
5406  <pre><code>
5407  var colModel = new Roo.grid.ColumnModel([
5408         {header: "Ticker", width: 60, sortable: true, locked: true},
5409         {header: "Company Name", width: 150, sortable: true},
5410         {header: "Market Cap.", width: 100, sortable: true},
5411         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5412         {header: "Employees", width: 100, sortable: true, resizable: false}
5413  ]);
5414  </code></pre>
5415  * <p>
5416  
5417  * The config options listed for this class are options which may appear in each
5418  * individual column definition.
5419  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5420  * @constructor
5421  * @param {Object} config An Array of column config objects. See this class's
5422  * config objects for details.
5423 */
5424 Roo.grid.ColumnModel = function(config){
5425         /**
5426      * The config passed into the constructor
5427      */
5428     this.config = config;
5429     this.lookup = {};
5430
5431     // if no id, create one
5432     // if the column does not have a dataIndex mapping,
5433     // map it to the order it is in the config
5434     for(var i = 0, len = config.length; i < len; i++){
5435         var c = config[i];
5436         if(typeof c.dataIndex == "undefined"){
5437             c.dataIndex = i;
5438         }
5439         if(typeof c.renderer == "string"){
5440             c.renderer = Roo.util.Format[c.renderer];
5441         }
5442         if(typeof c.id == "undefined"){
5443             c.id = Roo.id();
5444         }
5445         if(c.editor && c.editor.xtype){
5446             c.editor  = Roo.factory(c.editor, Roo.grid);
5447         }
5448         if(c.editor && c.editor.isFormField){
5449             c.editor = new Roo.grid.GridEditor(c.editor);
5450         }
5451         this.lookup[c.id] = c;
5452     }
5453
5454     /**
5455      * The width of columns which have no width specified (defaults to 100)
5456      * @type Number
5457      */
5458     this.defaultWidth = 100;
5459
5460     /**
5461      * Default sortable of columns which have no sortable specified (defaults to false)
5462      * @type Boolean
5463      */
5464     this.defaultSortable = false;
5465
5466     this.addEvents({
5467         /**
5468              * @event widthchange
5469              * Fires when the width of a column changes.
5470              * @param {ColumnModel} this
5471              * @param {Number} columnIndex The column index
5472              * @param {Number} newWidth The new width
5473              */
5474             "widthchange": true,
5475         /**
5476              * @event headerchange
5477              * Fires when the text of a header changes.
5478              * @param {ColumnModel} this
5479              * @param {Number} columnIndex The column index
5480              * @param {Number} newText The new header text
5481              */
5482             "headerchange": true,
5483         /**
5484              * @event hiddenchange
5485              * Fires when a column is hidden or "unhidden".
5486              * @param {ColumnModel} this
5487              * @param {Number} columnIndex The column index
5488              * @param {Boolean} hidden true if hidden, false otherwise
5489              */
5490             "hiddenchange": true,
5491             /**
5492          * @event columnmoved
5493          * Fires when a column is moved.
5494          * @param {ColumnModel} this
5495          * @param {Number} oldIndex
5496          * @param {Number} newIndex
5497          */
5498         "columnmoved" : true,
5499         /**
5500          * @event columlockchange
5501          * Fires when a column's locked state is changed
5502          * @param {ColumnModel} this
5503          * @param {Number} colIndex
5504          * @param {Boolean} locked true if locked
5505          */
5506         "columnlockchange" : true
5507     });
5508     Roo.grid.ColumnModel.superclass.constructor.call(this);
5509 };
5510 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5511     /**
5512      * @cfg {String} header The header text to display in the Grid view.
5513      */
5514     /**
5515      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5516      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5517      * specified, the column's index is used as an index into the Record's data Array.
5518      */
5519     /**
5520      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5521      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5522      */
5523     /**
5524      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5525      * Defaults to the value of the {@link #defaultSortable} property.
5526      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5527      */
5528     /**
5529      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5530      */
5531     /**
5532      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5533      */
5534     /**
5535      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5536      */
5537     /**
5538      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5539      */
5540     /**
5541      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5542      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5543      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5544      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5545      */
5546        /**
5547      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5548      */
5549     /**
5550      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5551      */
5552     /**
5553      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5554      */
5555     /**
5556      * @cfg {String} cursor (Optional)
5557      */
5558     /**
5559      * @cfg {String} tooltip (Optional)
5560      */
5561     /**
5562      * @cfg {Number} xs (Optional)
5563      */
5564     /**
5565      * @cfg {Number} sm (Optional)
5566      */
5567     /**
5568      * @cfg {Number} md (Optional)
5569      */
5570     /**
5571      * @cfg {Number} lg (Optional)
5572      */
5573     /**
5574      * Returns the id of the column at the specified index.
5575      * @param {Number} index The column index
5576      * @return {String} the id
5577      */
5578     getColumnId : function(index){
5579         return this.config[index].id;
5580     },
5581
5582     /**
5583      * Returns the column for a specified id.
5584      * @param {String} id The column id
5585      * @return {Object} the column
5586      */
5587     getColumnById : function(id){
5588         return this.lookup[id];
5589     },
5590
5591     
5592     /**
5593      * Returns the column for a specified dataIndex.
5594      * @param {String} dataIndex The column dataIndex
5595      * @return {Object|Boolean} the column or false if not found
5596      */
5597     getColumnByDataIndex: function(dataIndex){
5598         var index = this.findColumnIndex(dataIndex);
5599         return index > -1 ? this.config[index] : false;
5600     },
5601     
5602     /**
5603      * Returns the index for a specified column id.
5604      * @param {String} id The column id
5605      * @return {Number} the index, or -1 if not found
5606      */
5607     getIndexById : function(id){
5608         for(var i = 0, len = this.config.length; i < len; i++){
5609             if(this.config[i].id == id){
5610                 return i;
5611             }
5612         }
5613         return -1;
5614     },
5615     
5616     /**
5617      * Returns the index for a specified column dataIndex.
5618      * @param {String} dataIndex The column dataIndex
5619      * @return {Number} the index, or -1 if not found
5620      */
5621     
5622     findColumnIndex : function(dataIndex){
5623         for(var i = 0, len = this.config.length; i < len; i++){
5624             if(this.config[i].dataIndex == dataIndex){
5625                 return i;
5626             }
5627         }
5628         return -1;
5629     },
5630     
5631     
5632     moveColumn : function(oldIndex, newIndex){
5633         var c = this.config[oldIndex];
5634         this.config.splice(oldIndex, 1);
5635         this.config.splice(newIndex, 0, c);
5636         this.dataMap = null;
5637         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5638     },
5639
5640     isLocked : function(colIndex){
5641         return this.config[colIndex].locked === true;
5642     },
5643
5644     setLocked : function(colIndex, value, suppressEvent){
5645         if(this.isLocked(colIndex) == value){
5646             return;
5647         }
5648         this.config[colIndex].locked = value;
5649         if(!suppressEvent){
5650             this.fireEvent("columnlockchange", this, colIndex, value);
5651         }
5652     },
5653
5654     getTotalLockedWidth : function(){
5655         var totalWidth = 0;
5656         for(var i = 0; i < this.config.length; i++){
5657             if(this.isLocked(i) && !this.isHidden(i)){
5658                 this.totalWidth += this.getColumnWidth(i);
5659             }
5660         }
5661         return totalWidth;
5662     },
5663
5664     getLockedCount : function(){
5665         for(var i = 0, len = this.config.length; i < len; i++){
5666             if(!this.isLocked(i)){
5667                 return i;
5668             }
5669         }
5670         
5671         return this.config.length;
5672     },
5673
5674     /**
5675      * Returns the number of columns.
5676      * @return {Number}
5677      */
5678     getColumnCount : function(visibleOnly){
5679         if(visibleOnly === true){
5680             var c = 0;
5681             for(var i = 0, len = this.config.length; i < len; i++){
5682                 if(!this.isHidden(i)){
5683                     c++;
5684                 }
5685             }
5686             return c;
5687         }
5688         return this.config.length;
5689     },
5690
5691     /**
5692      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5693      * @param {Function} fn
5694      * @param {Object} scope (optional)
5695      * @return {Array} result
5696      */
5697     getColumnsBy : function(fn, scope){
5698         var r = [];
5699         for(var i = 0, len = this.config.length; i < len; i++){
5700             var c = this.config[i];
5701             if(fn.call(scope||this, c, i) === true){
5702                 r[r.length] = c;
5703             }
5704         }
5705         return r;
5706     },
5707
5708     /**
5709      * Returns true if the specified column is sortable.
5710      * @param {Number} col The column index
5711      * @return {Boolean}
5712      */
5713     isSortable : function(col){
5714         if(typeof this.config[col].sortable == "undefined"){
5715             return this.defaultSortable;
5716         }
5717         return this.config[col].sortable;
5718     },
5719
5720     /**
5721      * Returns the rendering (formatting) function defined for the column.
5722      * @param {Number} col The column index.
5723      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5724      */
5725     getRenderer : function(col){
5726         if(!this.config[col].renderer){
5727             return Roo.grid.ColumnModel.defaultRenderer;
5728         }
5729         return this.config[col].renderer;
5730     },
5731
5732     /**
5733      * Sets the rendering (formatting) function for a column.
5734      * @param {Number} col The column index
5735      * @param {Function} fn The function to use to process the cell's raw data
5736      * to return HTML markup for the grid view. The render function is called with
5737      * the following parameters:<ul>
5738      * <li>Data value.</li>
5739      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5740      * <li>css A CSS style string to apply to the table cell.</li>
5741      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5742      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5743      * <li>Row index</li>
5744      * <li>Column index</li>
5745      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5746      */
5747     setRenderer : function(col, fn){
5748         this.config[col].renderer = fn;
5749     },
5750
5751     /**
5752      * Returns the width for the specified column.
5753      * @param {Number} col The column index
5754      * @return {Number}
5755      */
5756     getColumnWidth : function(col){
5757         return this.config[col].width * 1 || this.defaultWidth;
5758     },
5759
5760     /**
5761      * Sets the width for a column.
5762      * @param {Number} col The column index
5763      * @param {Number} width The new width
5764      */
5765     setColumnWidth : function(col, width, suppressEvent){
5766         this.config[col].width = width;
5767         this.totalWidth = null;
5768         if(!suppressEvent){
5769              this.fireEvent("widthchange", this, col, width);
5770         }
5771     },
5772
5773     /**
5774      * Returns the total width of all columns.
5775      * @param {Boolean} includeHidden True to include hidden column widths
5776      * @return {Number}
5777      */
5778     getTotalWidth : function(includeHidden){
5779         if(!this.totalWidth){
5780             this.totalWidth = 0;
5781             for(var i = 0, len = this.config.length; i < len; i++){
5782                 if(includeHidden || !this.isHidden(i)){
5783                     this.totalWidth += this.getColumnWidth(i);
5784                 }
5785             }
5786         }
5787         return this.totalWidth;
5788     },
5789
5790     /**
5791      * Returns the header for the specified column.
5792      * @param {Number} col The column index
5793      * @return {String}
5794      */
5795     getColumnHeader : function(col){
5796         return this.config[col].header;
5797     },
5798
5799     /**
5800      * Sets the header for a column.
5801      * @param {Number} col The column index
5802      * @param {String} header The new header
5803      */
5804     setColumnHeader : function(col, header){
5805         this.config[col].header = header;
5806         this.fireEvent("headerchange", this, col, header);
5807     },
5808
5809     /**
5810      * Returns the tooltip for the specified column.
5811      * @param {Number} col The column index
5812      * @return {String}
5813      */
5814     getColumnTooltip : function(col){
5815             return this.config[col].tooltip;
5816     },
5817     /**
5818      * Sets the tooltip for a column.
5819      * @param {Number} col The column index
5820      * @param {String} tooltip The new tooltip
5821      */
5822     setColumnTooltip : function(col, tooltip){
5823             this.config[col].tooltip = tooltip;
5824     },
5825
5826     /**
5827      * Returns the dataIndex for the specified column.
5828      * @param {Number} col The column index
5829      * @return {Number}
5830      */
5831     getDataIndex : function(col){
5832         return this.config[col].dataIndex;
5833     },
5834
5835     /**
5836      * Sets the dataIndex for a column.
5837      * @param {Number} col The column index
5838      * @param {Number} dataIndex The new dataIndex
5839      */
5840     setDataIndex : function(col, dataIndex){
5841         this.config[col].dataIndex = dataIndex;
5842     },
5843
5844     
5845     
5846     /**
5847      * Returns true if the cell is editable.
5848      * @param {Number} colIndex The column index
5849      * @param {Number} rowIndex The row index - this is nto actually used..?
5850      * @return {Boolean}
5851      */
5852     isCellEditable : function(colIndex, rowIndex){
5853         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5854     },
5855
5856     /**
5857      * Returns the editor defined for the cell/column.
5858      * return false or null to disable editing.
5859      * @param {Number} colIndex The column index
5860      * @param {Number} rowIndex The row index
5861      * @return {Object}
5862      */
5863     getCellEditor : function(colIndex, rowIndex){
5864         return this.config[colIndex].editor;
5865     },
5866
5867     /**
5868      * Sets if a column is editable.
5869      * @param {Number} col The column index
5870      * @param {Boolean} editable True if the column is editable
5871      */
5872     setEditable : function(col, editable){
5873         this.config[col].editable = editable;
5874     },
5875
5876
5877     /**
5878      * Returns true if the column is hidden.
5879      * @param {Number} colIndex The column index
5880      * @return {Boolean}
5881      */
5882     isHidden : function(colIndex){
5883         return this.config[colIndex].hidden;
5884     },
5885
5886
5887     /**
5888      * Returns true if the column width cannot be changed
5889      */
5890     isFixed : function(colIndex){
5891         return this.config[colIndex].fixed;
5892     },
5893
5894     /**
5895      * Returns true if the column can be resized
5896      * @return {Boolean}
5897      */
5898     isResizable : function(colIndex){
5899         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5900     },
5901     /**
5902      * Sets if a column is hidden.
5903      * @param {Number} colIndex The column index
5904      * @param {Boolean} hidden True if the column is hidden
5905      */
5906     setHidden : function(colIndex, hidden){
5907         this.config[colIndex].hidden = hidden;
5908         this.totalWidth = null;
5909         this.fireEvent("hiddenchange", this, colIndex, hidden);
5910     },
5911
5912     /**
5913      * Sets the editor for a column.
5914      * @param {Number} col The column index
5915      * @param {Object} editor The editor object
5916      */
5917     setEditor : function(col, editor){
5918         this.config[col].editor = editor;
5919     }
5920 });
5921
5922 Roo.grid.ColumnModel.defaultRenderer = function(value)
5923 {
5924     if(typeof value == "object") {
5925         return value;
5926     }
5927         if(typeof value == "string" && value.length < 1){
5928             return "&#160;";
5929         }
5930     
5931         return String.format("{0}", value);
5932 };
5933
5934 // Alias for backwards compatibility
5935 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5936 /*
5937  * Based on:
5938  * Ext JS Library 1.1.1
5939  * Copyright(c) 2006-2007, Ext JS, LLC.
5940  *
5941  * Originally Released Under LGPL - original licence link has changed is not relivant.
5942  *
5943  * Fork - LGPL
5944  * <script type="text/javascript">
5945  */
5946  
5947 /**
5948  * @class Roo.LoadMask
5949  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5950  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5951  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5952  * element's UpdateManager load indicator and will be destroyed after the initial load.
5953  * @constructor
5954  * Create a new LoadMask
5955  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5956  * @param {Object} config The config object
5957  */
5958 Roo.LoadMask = function(el, config){
5959     this.el = Roo.get(el);
5960     Roo.apply(this, config);
5961     if(this.store){
5962         this.store.on('beforeload', this.onBeforeLoad, this);
5963         this.store.on('load', this.onLoad, this);
5964         this.store.on('loadexception', this.onLoadException, this);
5965         this.removeMask = false;
5966     }else{
5967         var um = this.el.getUpdateManager();
5968         um.showLoadIndicator = false; // disable the default indicator
5969         um.on('beforeupdate', this.onBeforeLoad, this);
5970         um.on('update', this.onLoad, this);
5971         um.on('failure', this.onLoad, this);
5972         this.removeMask = true;
5973     }
5974 };
5975
5976 Roo.LoadMask.prototype = {
5977     /**
5978      * @cfg {Boolean} removeMask
5979      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5980      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5981      */
5982     /**
5983      * @cfg {String} msg
5984      * The text to display in a centered loading message box (defaults to 'Loading...')
5985      */
5986     msg : 'Loading...',
5987     /**
5988      * @cfg {String} msgCls
5989      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5990      */
5991     msgCls : 'x-mask-loading',
5992
5993     /**
5994      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5995      * @type Boolean
5996      */
5997     disabled: false,
5998
5999     /**
6000      * Disables the mask to prevent it from being displayed
6001      */
6002     disable : function(){
6003        this.disabled = true;
6004     },
6005
6006     /**
6007      * Enables the mask so that it can be displayed
6008      */
6009     enable : function(){
6010         this.disabled = false;
6011     },
6012     
6013     onLoadException : function()
6014     {
6015         Roo.log(arguments);
6016         
6017         if (typeof(arguments[3]) != 'undefined') {
6018             Roo.MessageBox.alert("Error loading",arguments[3]);
6019         } 
6020         /*
6021         try {
6022             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6023                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6024             }   
6025         } catch(e) {
6026             
6027         }
6028         */
6029     
6030         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6031     },
6032     // private
6033     onLoad : function()
6034     {
6035         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6036     },
6037
6038     // private
6039     onBeforeLoad : function(){
6040         if(!this.disabled){
6041             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6042         }
6043     },
6044
6045     // private
6046     destroy : function(){
6047         if(this.store){
6048             this.store.un('beforeload', this.onBeforeLoad, this);
6049             this.store.un('load', this.onLoad, this);
6050             this.store.un('loadexception', this.onLoadException, this);
6051         }else{
6052             var um = this.el.getUpdateManager();
6053             um.un('beforeupdate', this.onBeforeLoad, this);
6054             um.un('update', this.onLoad, this);
6055             um.un('failure', this.onLoad, this);
6056         }
6057     }
6058 };/*
6059  * - LGPL
6060  *
6061  * table
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.Table
6067  * @extends Roo.bootstrap.Component
6068  * Bootstrap Table class
6069  * @cfg {String} cls table class
6070  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6071  * @cfg {String} bgcolor Specifies the background color for a table
6072  * @cfg {Number} border Specifies whether the table cells should have borders or not
6073  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6074  * @cfg {Number} cellspacing Specifies the space between cells
6075  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6076  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6077  * @cfg {String} sortable Specifies that the table should be sortable
6078  * @cfg {String} summary Specifies a summary of the content of a table
6079  * @cfg {Number} width Specifies the width of a table
6080  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6081  * 
6082  * @cfg {boolean} striped Should the rows be alternative striped
6083  * @cfg {boolean} bordered Add borders to the table
6084  * @cfg {boolean} hover Add hover highlighting
6085  * @cfg {boolean} condensed Format condensed
6086  * @cfg {boolean} responsive Format condensed
6087  * @cfg {Boolean} loadMask (true|false) default false
6088  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6089  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6090  * @cfg {Boolean} rowSelection (true|false) default false
6091  * @cfg {Boolean} cellSelection (true|false) default false
6092  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6093  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6094  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6095  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6096  
6097  * 
6098  * @constructor
6099  * Create a new Table
6100  * @param {Object} config The config object
6101  */
6102
6103 Roo.bootstrap.Table = function(config){
6104     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6105     
6106   
6107     
6108     // BC...
6109     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6110     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6111     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6112     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6113     
6114     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6115     if (this.sm) {
6116         this.sm.grid = this;
6117         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6118         this.sm = this.selModel;
6119         this.sm.xmodule = this.xmodule || false;
6120     }
6121     
6122     if (this.cm && typeof(this.cm.config) == 'undefined') {
6123         this.colModel = new Roo.grid.ColumnModel(this.cm);
6124         this.cm = this.colModel;
6125         this.cm.xmodule = this.xmodule || false;
6126     }
6127     if (this.store) {
6128         this.store= Roo.factory(this.store, Roo.data);
6129         this.ds = this.store;
6130         this.ds.xmodule = this.xmodule || false;
6131          
6132     }
6133     if (this.footer && this.store) {
6134         this.footer.dataSource = this.ds;
6135         this.footer = Roo.factory(this.footer);
6136     }
6137     
6138     /** @private */
6139     this.addEvents({
6140         /**
6141          * @event cellclick
6142          * Fires when a cell is clicked
6143          * @param {Roo.bootstrap.Table} this
6144          * @param {Roo.Element} el
6145          * @param {Number} rowIndex
6146          * @param {Number} columnIndex
6147          * @param {Roo.EventObject} e
6148          */
6149         "cellclick" : true,
6150         /**
6151          * @event celldblclick
6152          * Fires when a cell is double clicked
6153          * @param {Roo.bootstrap.Table} this
6154          * @param {Roo.Element} el
6155          * @param {Number} rowIndex
6156          * @param {Number} columnIndex
6157          * @param {Roo.EventObject} e
6158          */
6159         "celldblclick" : true,
6160         /**
6161          * @event rowclick
6162          * Fires when a row is clicked
6163          * @param {Roo.bootstrap.Table} this
6164          * @param {Roo.Element} el
6165          * @param {Number} rowIndex
6166          * @param {Roo.EventObject} e
6167          */
6168         "rowclick" : true,
6169         /**
6170          * @event rowdblclick
6171          * Fires when a row is double clicked
6172          * @param {Roo.bootstrap.Table} this
6173          * @param {Roo.Element} el
6174          * @param {Number} rowIndex
6175          * @param {Roo.EventObject} e
6176          */
6177         "rowdblclick" : true,
6178         /**
6179          * @event mouseover
6180          * Fires when a mouseover occur
6181          * @param {Roo.bootstrap.Table} this
6182          * @param {Roo.Element} el
6183          * @param {Number} rowIndex
6184          * @param {Number} columnIndex
6185          * @param {Roo.EventObject} e
6186          */
6187         "mouseover" : true,
6188         /**
6189          * @event mouseout
6190          * Fires when a mouseout occur
6191          * @param {Roo.bootstrap.Table} this
6192          * @param {Roo.Element} el
6193          * @param {Number} rowIndex
6194          * @param {Number} columnIndex
6195          * @param {Roo.EventObject} e
6196          */
6197         "mouseout" : true,
6198         /**
6199          * @event rowclass
6200          * Fires when a row is rendered, so you can change add a style to it.
6201          * @param {Roo.bootstrap.Table} this
6202          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6203          */
6204         'rowclass' : true,
6205           /**
6206          * @event rowsrendered
6207          * Fires when all the  rows have been rendered
6208          * @param {Roo.bootstrap.Table} this
6209          */
6210         'rowsrendered' : true,
6211         /**
6212          * @event contextmenu
6213          * The raw contextmenu event for the entire grid.
6214          * @param {Roo.EventObject} e
6215          */
6216         "contextmenu" : true,
6217         /**
6218          * @event rowcontextmenu
6219          * Fires when a row is right clicked
6220          * @param {Roo.bootstrap.Table} this
6221          * @param {Number} rowIndex
6222          * @param {Roo.EventObject} e
6223          */
6224         "rowcontextmenu" : true,
6225         /**
6226          * @event cellcontextmenu
6227          * Fires when a cell is right clicked
6228          * @param {Roo.bootstrap.Table} this
6229          * @param {Number} rowIndex
6230          * @param {Number} cellIndex
6231          * @param {Roo.EventObject} e
6232          */
6233          "cellcontextmenu" : true,
6234          /**
6235          * @event headercontextmenu
6236          * Fires when a header is right clicked
6237          * @param {Roo.bootstrap.Table} this
6238          * @param {Number} columnIndex
6239          * @param {Roo.EventObject} e
6240          */
6241         "headercontextmenu" : true
6242     });
6243 };
6244
6245 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6246     
6247     cls: false,
6248     align: false,
6249     bgcolor: false,
6250     border: false,
6251     cellpadding: false,
6252     cellspacing: false,
6253     frame: false,
6254     rules: false,
6255     sortable: false,
6256     summary: false,
6257     width: false,
6258     striped : false,
6259     scrollBody : false,
6260     bordered: false,
6261     hover:  false,
6262     condensed : false,
6263     responsive : false,
6264     sm : false,
6265     cm : false,
6266     store : false,
6267     loadMask : false,
6268     footerShow : true,
6269     headerShow : true,
6270   
6271     rowSelection : false,
6272     cellSelection : false,
6273     layout : false,
6274     
6275     // Roo.Element - the tbody
6276     mainBody: false,
6277     // Roo.Element - thead element
6278     mainHead: false,
6279     
6280     container: false, // used by gridpanel...
6281     
6282     lazyLoad : false,
6283     
6284     CSS : Roo.util.CSS,
6285     
6286     auto_hide_footer : false,
6287     
6288     getAutoCreate : function()
6289     {
6290         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6291         
6292         cfg = {
6293             tag: 'table',
6294             cls : 'table',
6295             cn : []
6296         };
6297         if (this.scrollBody) {
6298             cfg.cls += ' table-body-fixed';
6299         }    
6300         if (this.striped) {
6301             cfg.cls += ' table-striped';
6302         }
6303         
6304         if (this.hover) {
6305             cfg.cls += ' table-hover';
6306         }
6307         if (this.bordered) {
6308             cfg.cls += ' table-bordered';
6309         }
6310         if (this.condensed) {
6311             cfg.cls += ' table-condensed';
6312         }
6313         if (this.responsive) {
6314             cfg.cls += ' table-responsive';
6315         }
6316         
6317         if (this.cls) {
6318             cfg.cls+=  ' ' +this.cls;
6319         }
6320         
6321         // this lot should be simplifed...
6322         var _t = this;
6323         var cp = [
6324             'align',
6325             'bgcolor',
6326             'border',
6327             'cellpadding',
6328             'cellspacing',
6329             'frame',
6330             'rules',
6331             'sortable',
6332             'summary',
6333             'width'
6334         ].forEach(function(k) {
6335             if (_t[k]) {
6336                 cfg[k] = _t[k];
6337             }
6338         });
6339         
6340         
6341         if (this.layout) {
6342             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6343         }
6344         
6345         if(this.store || this.cm){
6346             if(this.headerShow){
6347                 cfg.cn.push(this.renderHeader());
6348             }
6349             
6350             cfg.cn.push(this.renderBody());
6351             
6352             if(this.footerShow){
6353                 cfg.cn.push(this.renderFooter());
6354             }
6355             // where does this come from?
6356             //cfg.cls+=  ' TableGrid';
6357         }
6358         
6359         return { cn : [ cfg ] };
6360     },
6361     
6362     initEvents : function()
6363     {   
6364         if(!this.store || !this.cm){
6365             return;
6366         }
6367         if (this.selModel) {
6368             this.selModel.initEvents();
6369         }
6370         
6371         
6372         //Roo.log('initEvents with ds!!!!');
6373         
6374         this.mainBody = this.el.select('tbody', true).first();
6375         this.mainHead = this.el.select('thead', true).first();
6376         this.mainFoot = this.el.select('tfoot', true).first();
6377         
6378         
6379         
6380         var _this = this;
6381         
6382         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6383             e.on('click', _this.sort, _this);
6384         });
6385         
6386         this.mainBody.on("click", this.onClick, this);
6387         this.mainBody.on("dblclick", this.onDblClick, this);
6388         
6389         // why is this done????? = it breaks dialogs??
6390         //this.parent().el.setStyle('position', 'relative');
6391         
6392         
6393         if (this.footer) {
6394             this.footer.parentId = this.id;
6395             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6396             
6397             if(this.lazyLoad){
6398                 this.el.select('tfoot tr td').first().addClass('hide');
6399             }
6400         } 
6401         
6402         if(this.loadMask) {
6403             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6404         }
6405         
6406         this.store.on('load', this.onLoad, this);
6407         this.store.on('beforeload', this.onBeforeLoad, this);
6408         this.store.on('update', this.onUpdate, this);
6409         this.store.on('add', this.onAdd, this);
6410         this.store.on("clear", this.clear, this);
6411         
6412         this.el.on("contextmenu", this.onContextMenu, this);
6413         
6414         this.mainBody.on('scroll', this.onBodyScroll, this);
6415         
6416         this.cm.on("headerchange", this.onHeaderChange, this);
6417         
6418         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6419         
6420     },
6421     
6422     onContextMenu : function(e, t)
6423     {
6424         this.processEvent("contextmenu", e);
6425     },
6426     
6427     processEvent : function(name, e)
6428     {
6429         if (name != 'touchstart' ) {
6430             this.fireEvent(name, e);    
6431         }
6432         
6433         var t = e.getTarget();
6434         
6435         var cell = Roo.get(t);
6436         
6437         if(!cell){
6438             return;
6439         }
6440         
6441         if(cell.findParent('tfoot', false, true)){
6442             return;
6443         }
6444         
6445         if(cell.findParent('thead', false, true)){
6446             
6447             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6448                 cell = Roo.get(t).findParent('th', false, true);
6449                 if (!cell) {
6450                     Roo.log("failed to find th in thead?");
6451                     Roo.log(e.getTarget());
6452                     return;
6453                 }
6454             }
6455             
6456             var cellIndex = cell.dom.cellIndex;
6457             
6458             var ename = name == 'touchstart' ? 'click' : name;
6459             this.fireEvent("header" + ename, this, cellIndex, e);
6460             
6461             return;
6462         }
6463         
6464         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6465             cell = Roo.get(t).findParent('td', false, true);
6466             if (!cell) {
6467                 Roo.log("failed to find th in tbody?");
6468                 Roo.log(e.getTarget());
6469                 return;
6470             }
6471         }
6472         
6473         var row = cell.findParent('tr', false, true);
6474         var cellIndex = cell.dom.cellIndex;
6475         var rowIndex = row.dom.rowIndex - 1;
6476         
6477         if(row !== false){
6478             
6479             this.fireEvent("row" + name, this, rowIndex, e);
6480             
6481             if(cell !== false){
6482             
6483                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6484             }
6485         }
6486         
6487     },
6488     
6489     onMouseover : function(e, el)
6490     {
6491         var cell = Roo.get(el);
6492         
6493         if(!cell){
6494             return;
6495         }
6496         
6497         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6498             cell = cell.findParent('td', false, true);
6499         }
6500         
6501         var row = cell.findParent('tr', false, true);
6502         var cellIndex = cell.dom.cellIndex;
6503         var rowIndex = row.dom.rowIndex - 1; // start from 0
6504         
6505         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6506         
6507     },
6508     
6509     onMouseout : function(e, el)
6510     {
6511         var cell = Roo.get(el);
6512         
6513         if(!cell){
6514             return;
6515         }
6516         
6517         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6518             cell = cell.findParent('td', false, true);
6519         }
6520         
6521         var row = cell.findParent('tr', false, true);
6522         var cellIndex = cell.dom.cellIndex;
6523         var rowIndex = row.dom.rowIndex - 1; // start from 0
6524         
6525         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6526         
6527     },
6528     
6529     onClick : function(e, el)
6530     {
6531         var cell = Roo.get(el);
6532         
6533         if(!cell || (!this.cellSelection && !this.rowSelection)){
6534             return;
6535         }
6536         
6537         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6538             cell = cell.findParent('td', false, true);
6539         }
6540         
6541         if(!cell || typeof(cell) == 'undefined'){
6542             return;
6543         }
6544         
6545         var row = cell.findParent('tr', false, true);
6546         
6547         if(!row || typeof(row) == 'undefined'){
6548             return;
6549         }
6550         
6551         var cellIndex = cell.dom.cellIndex;
6552         var rowIndex = this.getRowIndex(row);
6553         
6554         // why??? - should these not be based on SelectionModel?
6555         if(this.cellSelection){
6556             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6557         }
6558         
6559         if(this.rowSelection){
6560             this.fireEvent('rowclick', this, row, rowIndex, e);
6561         }
6562         
6563         
6564     },
6565         
6566     onDblClick : function(e,el)
6567     {
6568         var cell = Roo.get(el);
6569         
6570         if(!cell || (!this.cellSelection && !this.rowSelection)){
6571             return;
6572         }
6573         
6574         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6575             cell = cell.findParent('td', false, true);
6576         }
6577         
6578         if(!cell || typeof(cell) == 'undefined'){
6579             return;
6580         }
6581         
6582         var row = cell.findParent('tr', false, true);
6583         
6584         if(!row || typeof(row) == 'undefined'){
6585             return;
6586         }
6587         
6588         var cellIndex = cell.dom.cellIndex;
6589         var rowIndex = this.getRowIndex(row);
6590         
6591         if(this.cellSelection){
6592             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6593         }
6594         
6595         if(this.rowSelection){
6596             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6597         }
6598     },
6599     
6600     sort : function(e,el)
6601     {
6602         var col = Roo.get(el);
6603         
6604         if(!col.hasClass('sortable')){
6605             return;
6606         }
6607         
6608         var sort = col.attr('sort');
6609         var dir = 'ASC';
6610         
6611         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6612             dir = 'DESC';
6613         }
6614         
6615         this.store.sortInfo = {field : sort, direction : dir};
6616         
6617         if (this.footer) {
6618             Roo.log("calling footer first");
6619             this.footer.onClick('first');
6620         } else {
6621         
6622             this.store.load({ params : { start : 0 } });
6623         }
6624     },
6625     
6626     renderHeader : function()
6627     {
6628         var header = {
6629             tag: 'thead',
6630             cn : []
6631         };
6632         
6633         var cm = this.cm;
6634         this.totalWidth = 0;
6635         
6636         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6637             
6638             var config = cm.config[i];
6639             
6640             var c = {
6641                 tag: 'th',
6642                 cls : 'x-hcol-' + i,
6643                 style : '',
6644                 html: cm.getColumnHeader(i)
6645             };
6646             
6647             var hh = '';
6648             
6649             if(typeof(config.sortable) != 'undefined' && config.sortable){
6650                 c.cls = 'sortable';
6651                 c.html = '<i class="glyphicon"></i>' + c.html;
6652             }
6653             
6654             if(typeof(config.lgHeader) != 'undefined'){
6655                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6656             }
6657             
6658             if(typeof(config.mdHeader) != 'undefined'){
6659                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6660             }
6661             
6662             if(typeof(config.smHeader) != 'undefined'){
6663                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6664             }
6665             
6666             if(typeof(config.xsHeader) != 'undefined'){
6667                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6668             }
6669             
6670             if(hh.length){
6671                 c.html = hh;
6672             }
6673             
6674             if(typeof(config.tooltip) != 'undefined'){
6675                 c.tooltip = config.tooltip;
6676             }
6677             
6678             if(typeof(config.colspan) != 'undefined'){
6679                 c.colspan = config.colspan;
6680             }
6681             
6682             if(typeof(config.hidden) != 'undefined' && config.hidden){
6683                 c.style += ' display:none;';
6684             }
6685             
6686             if(typeof(config.dataIndex) != 'undefined'){
6687                 c.sort = config.dataIndex;
6688             }
6689             
6690            
6691             
6692             if(typeof(config.align) != 'undefined' && config.align.length){
6693                 c.style += ' text-align:' + config.align + ';';
6694             }
6695             
6696             if(typeof(config.width) != 'undefined'){
6697                 c.style += ' width:' + config.width + 'px;';
6698                 this.totalWidth += config.width;
6699             } else {
6700                 this.totalWidth += 100; // assume minimum of 100 per column?
6701             }
6702             
6703             if(typeof(config.cls) != 'undefined'){
6704                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6705             }
6706             
6707             ['xs','sm','md','lg'].map(function(size){
6708                 
6709                 if(typeof(config[size]) == 'undefined'){
6710                     return;
6711                 }
6712                 
6713                 if (!config[size]) { // 0 = hidden
6714                     c.cls += ' hidden-' + size;
6715                     return;
6716                 }
6717                 
6718                 c.cls += ' col-' + size + '-' + config[size];
6719
6720             });
6721             
6722             header.cn.push(c)
6723         }
6724         
6725         return header;
6726     },
6727     
6728     renderBody : function()
6729     {
6730         var body = {
6731             tag: 'tbody',
6732             cn : [
6733                 {
6734                     tag: 'tr',
6735                     cn : [
6736                         {
6737                             tag : 'td',
6738                             colspan :  this.cm.getColumnCount()
6739                         }
6740                     ]
6741                 }
6742             ]
6743         };
6744         
6745         return body;
6746     },
6747     
6748     renderFooter : function()
6749     {
6750         var footer = {
6751             tag: 'tfoot',
6752             cn : [
6753                 {
6754                     tag: 'tr',
6755                     cn : [
6756                         {
6757                             tag : 'td',
6758                             colspan :  this.cm.getColumnCount()
6759                         }
6760                     ]
6761                 }
6762             ]
6763         };
6764         
6765         return footer;
6766     },
6767     
6768     
6769     
6770     onLoad : function()
6771     {
6772 //        Roo.log('ds onload');
6773         this.clear();
6774         
6775         var _this = this;
6776         var cm = this.cm;
6777         var ds = this.store;
6778         
6779         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6780             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6781             if (_this.store.sortInfo) {
6782                     
6783                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6784                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6785                 }
6786                 
6787                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6788                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6789                 }
6790             }
6791         });
6792         
6793         var tbody =  this.mainBody;
6794               
6795         if(ds.getCount() > 0){
6796             ds.data.each(function(d,rowIndex){
6797                 var row =  this.renderRow(cm, ds, rowIndex);
6798                 
6799                 tbody.createChild(row);
6800                 
6801                 var _this = this;
6802                 
6803                 if(row.cellObjects.length){
6804                     Roo.each(row.cellObjects, function(r){
6805                         _this.renderCellObject(r);
6806                     })
6807                 }
6808                 
6809             }, this);
6810         }
6811         
6812         var tfoot = this.el.select('tfoot', true).first();
6813         
6814         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6815             
6816             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6817             
6818             var total = this.ds.getTotalCount();
6819             
6820             if(this.footer.pageSize < total){
6821                 this.mainFoot.show();
6822             }
6823         }
6824         
6825         Roo.each(this.el.select('tbody td', true).elements, function(e){
6826             e.on('mouseover', _this.onMouseover, _this);
6827         });
6828         
6829         Roo.each(this.el.select('tbody td', true).elements, function(e){
6830             e.on('mouseout', _this.onMouseout, _this);
6831         });
6832         this.fireEvent('rowsrendered', this);
6833         
6834         this.autoSize();
6835     },
6836     
6837     
6838     onUpdate : function(ds,record)
6839     {
6840         this.refreshRow(record);
6841         this.autoSize();
6842     },
6843     
6844     onRemove : function(ds, record, index, isUpdate){
6845         if(isUpdate !== true){
6846             this.fireEvent("beforerowremoved", this, index, record);
6847         }
6848         var bt = this.mainBody.dom;
6849         
6850         var rows = this.el.select('tbody > tr', true).elements;
6851         
6852         if(typeof(rows[index]) != 'undefined'){
6853             bt.removeChild(rows[index].dom);
6854         }
6855         
6856 //        if(bt.rows[index]){
6857 //            bt.removeChild(bt.rows[index]);
6858 //        }
6859         
6860         if(isUpdate !== true){
6861             //this.stripeRows(index);
6862             //this.syncRowHeights(index, index);
6863             //this.layout();
6864             this.fireEvent("rowremoved", this, index, record);
6865         }
6866     },
6867     
6868     onAdd : function(ds, records, rowIndex)
6869     {
6870         //Roo.log('on Add called');
6871         // - note this does not handle multiple adding very well..
6872         var bt = this.mainBody.dom;
6873         for (var i =0 ; i < records.length;i++) {
6874             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6875             //Roo.log(records[i]);
6876             //Roo.log(this.store.getAt(rowIndex+i));
6877             this.insertRow(this.store, rowIndex + i, false);
6878             return;
6879         }
6880         
6881     },
6882     
6883     
6884     refreshRow : function(record){
6885         var ds = this.store, index;
6886         if(typeof record == 'number'){
6887             index = record;
6888             record = ds.getAt(index);
6889         }else{
6890             index = ds.indexOf(record);
6891         }
6892         this.insertRow(ds, index, true);
6893         this.autoSize();
6894         this.onRemove(ds, record, index+1, true);
6895         this.autoSize();
6896         //this.syncRowHeights(index, index);
6897         //this.layout();
6898         this.fireEvent("rowupdated", this, index, record);
6899     },
6900     
6901     insertRow : function(dm, rowIndex, isUpdate){
6902         
6903         if(!isUpdate){
6904             this.fireEvent("beforerowsinserted", this, rowIndex);
6905         }
6906             //var s = this.getScrollState();
6907         var row = this.renderRow(this.cm, this.store, rowIndex);
6908         // insert before rowIndex..
6909         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6910         
6911         var _this = this;
6912                 
6913         if(row.cellObjects.length){
6914             Roo.each(row.cellObjects, function(r){
6915                 _this.renderCellObject(r);
6916             })
6917         }
6918             
6919         if(!isUpdate){
6920             this.fireEvent("rowsinserted", this, rowIndex);
6921             //this.syncRowHeights(firstRow, lastRow);
6922             //this.stripeRows(firstRow);
6923             //this.layout();
6924         }
6925         
6926     },
6927     
6928     
6929     getRowDom : function(rowIndex)
6930     {
6931         var rows = this.el.select('tbody > tr', true).elements;
6932         
6933         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6934         
6935     },
6936     // returns the object tree for a tr..
6937   
6938     
6939     renderRow : function(cm, ds, rowIndex) 
6940     {
6941         var d = ds.getAt(rowIndex);
6942         
6943         var row = {
6944             tag : 'tr',
6945             cls : 'x-row-' + rowIndex,
6946             cn : []
6947         };
6948             
6949         var cellObjects = [];
6950         
6951         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6952             var config = cm.config[i];
6953             
6954             var renderer = cm.getRenderer(i);
6955             var value = '';
6956             var id = false;
6957             
6958             if(typeof(renderer) !== 'undefined'){
6959                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6960             }
6961             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6962             // and are rendered into the cells after the row is rendered - using the id for the element.
6963             
6964             if(typeof(value) === 'object'){
6965                 id = Roo.id();
6966                 cellObjects.push({
6967                     container : id,
6968                     cfg : value 
6969                 })
6970             }
6971             
6972             var rowcfg = {
6973                 record: d,
6974                 rowIndex : rowIndex,
6975                 colIndex : i,
6976                 rowClass : ''
6977             };
6978
6979             this.fireEvent('rowclass', this, rowcfg);
6980             
6981             var td = {
6982                 tag: 'td',
6983                 cls : rowcfg.rowClass + ' x-col-' + i,
6984                 style: '',
6985                 html: (typeof(value) === 'object') ? '' : value
6986             };
6987             
6988             if (id) {
6989                 td.id = id;
6990             }
6991             
6992             if(typeof(config.colspan) != 'undefined'){
6993                 td.colspan = config.colspan;
6994             }
6995             
6996             if(typeof(config.hidden) != 'undefined' && config.hidden){
6997                 td.style += ' display:none;';
6998             }
6999             
7000             if(typeof(config.align) != 'undefined' && config.align.length){
7001                 td.style += ' text-align:' + config.align + ';';
7002             }
7003             if(typeof(config.valign) != 'undefined' && config.valign.length){
7004                 td.style += ' vertical-align:' + config.valign + ';';
7005             }
7006             
7007             if(typeof(config.width) != 'undefined'){
7008                 td.style += ' width:' +  config.width + 'px;';
7009             }
7010             
7011             if(typeof(config.cursor) != 'undefined'){
7012                 td.style += ' cursor:' +  config.cursor + ';';
7013             }
7014             
7015             if(typeof(config.cls) != 'undefined'){
7016                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7017             }
7018             
7019             ['xs','sm','md','lg'].map(function(size){
7020                 
7021                 if(typeof(config[size]) == 'undefined'){
7022                     return;
7023                 }
7024                 
7025                 if (!config[size]) { // 0 = hidden
7026                     td.cls += ' hidden-' + size;
7027                     return;
7028                 }
7029                 
7030                 td.cls += ' col-' + size + '-' + config[size];
7031
7032             });
7033             
7034             row.cn.push(td);
7035            
7036         }
7037         
7038         row.cellObjects = cellObjects;
7039         
7040         return row;
7041           
7042     },
7043     
7044     
7045     
7046     onBeforeLoad : function()
7047     {
7048         
7049     },
7050      /**
7051      * Remove all rows
7052      */
7053     clear : function()
7054     {
7055         this.el.select('tbody', true).first().dom.innerHTML = '';
7056     },
7057     /**
7058      * Show or hide a row.
7059      * @param {Number} rowIndex to show or hide
7060      * @param {Boolean} state hide
7061      */
7062     setRowVisibility : function(rowIndex, state)
7063     {
7064         var bt = this.mainBody.dom;
7065         
7066         var rows = this.el.select('tbody > tr', true).elements;
7067         
7068         if(typeof(rows[rowIndex]) == 'undefined'){
7069             return;
7070         }
7071         rows[rowIndex].dom.style.display = state ? '' : 'none';
7072     },
7073     
7074     
7075     getSelectionModel : function(){
7076         if(!this.selModel){
7077             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7078         }
7079         return this.selModel;
7080     },
7081     /*
7082      * Render the Roo.bootstrap object from renderder
7083      */
7084     renderCellObject : function(r)
7085     {
7086         var _this = this;
7087         
7088         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7089         
7090         var t = r.cfg.render(r.container);
7091         
7092         if(r.cfg.cn){
7093             Roo.each(r.cfg.cn, function(c){
7094                 var child = {
7095                     container: t.getChildContainer(),
7096                     cfg: c
7097                 };
7098                 _this.renderCellObject(child);
7099             })
7100         }
7101     },
7102     
7103     getRowIndex : function(row)
7104     {
7105         var rowIndex = -1;
7106         
7107         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7108             if(el != row){
7109                 return;
7110             }
7111             
7112             rowIndex = index;
7113         });
7114         
7115         return rowIndex;
7116     },
7117      /**
7118      * Returns the grid's underlying element = used by panel.Grid
7119      * @return {Element} The element
7120      */
7121     getGridEl : function(){
7122         return this.el;
7123     },
7124      /**
7125      * Forces a resize - used by panel.Grid
7126      * @return {Element} The element
7127      */
7128     autoSize : function()
7129     {
7130         //var ctr = Roo.get(this.container.dom.parentElement);
7131         var ctr = Roo.get(this.el.dom);
7132         
7133         var thd = this.getGridEl().select('thead',true).first();
7134         var tbd = this.getGridEl().select('tbody', true).first();
7135         var tfd = this.getGridEl().select('tfoot', true).first();
7136         
7137         var cw = ctr.getWidth();
7138         
7139         if (tbd) {
7140             
7141             tbd.setSize(ctr.getWidth(),
7142                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7143             );
7144             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7145             cw -= barsize;
7146         }
7147         cw = Math.max(cw, this.totalWidth);
7148         this.getGridEl().select('tr',true).setWidth(cw);
7149         // resize 'expandable coloumn?
7150         
7151         return; // we doe not have a view in this design..
7152         
7153     },
7154     onBodyScroll: function()
7155     {
7156         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7157         if(this.mainHead){
7158             this.mainHead.setStyle({
7159                 'position' : 'relative',
7160                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7161             });
7162         }
7163         
7164         if(this.lazyLoad){
7165             
7166             var scrollHeight = this.mainBody.dom.scrollHeight;
7167             
7168             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7169             
7170             var height = this.mainBody.getHeight();
7171             
7172             if(scrollHeight - height == scrollTop) {
7173                 
7174                 var total = this.ds.getTotalCount();
7175                 
7176                 if(this.footer.cursor + this.footer.pageSize < total){
7177                     
7178                     this.footer.ds.load({
7179                         params : {
7180                             start : this.footer.cursor + this.footer.pageSize,
7181                             limit : this.footer.pageSize
7182                         },
7183                         add : true
7184                     });
7185                 }
7186             }
7187             
7188         }
7189     },
7190     
7191     onHeaderChange : function()
7192     {
7193         var header = this.renderHeader();
7194         var table = this.el.select('table', true).first();
7195         
7196         this.mainHead.remove();
7197         this.mainHead = table.createChild(header, this.mainBody, false);
7198     },
7199     
7200     onHiddenChange : function(colModel, colIndex, hidden)
7201     {
7202         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7203         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7204         
7205         this.CSS.updateRule(thSelector, "display", "");
7206         this.CSS.updateRule(tdSelector, "display", "");
7207         
7208         if(hidden){
7209             this.CSS.updateRule(thSelector, "display", "none");
7210             this.CSS.updateRule(tdSelector, "display", "none");
7211         }
7212         
7213         this.onHeaderChange();
7214         this.onLoad();
7215     },
7216     
7217     setColumnWidth: function(col_index, width)
7218     {
7219         // width = "md-2 xs-2..."
7220         if(!this.colModel.config[col_index]) {
7221             return;
7222         }
7223         
7224         var w = width.split(" ");
7225         
7226         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7227         
7228         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7229         
7230         
7231         for(var j = 0; j < w.length; j++) {
7232             
7233             if(!w[j]) {
7234                 continue;
7235             }
7236             
7237             var size_cls = w[j].split("-");
7238             
7239             if(!Number.isInteger(size_cls[1] * 1)) {
7240                 continue;
7241             }
7242             
7243             if(!this.colModel.config[col_index][size_cls[0]]) {
7244                 continue;
7245             }
7246             
7247             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7248                 continue;
7249             }
7250             
7251             h_row[0].classList.replace(
7252                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7253                 "col-"+size_cls[0]+"-"+size_cls[1]
7254             );
7255             
7256             for(var i = 0; i < rows.length; i++) {
7257                 
7258                 var size_cls = w[j].split("-");
7259                 
7260                 if(!Number.isInteger(size_cls[1] * 1)) {
7261                     continue;
7262                 }
7263                 
7264                 if(!this.colModel.config[col_index][size_cls[0]]) {
7265                     continue;
7266                 }
7267                 
7268                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7269                     continue;
7270                 }
7271                 
7272                 rows[i].classList.replace(
7273                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7274                     "col-"+size_cls[0]+"-"+size_cls[1]
7275                 );
7276             }
7277             
7278             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7279         }
7280     }
7281 });
7282
7283  
7284
7285  /*
7286  * - LGPL
7287  *
7288  * table cell
7289  * 
7290  */
7291
7292 /**
7293  * @class Roo.bootstrap.TableCell
7294  * @extends Roo.bootstrap.Component
7295  * Bootstrap TableCell class
7296  * @cfg {String} html cell contain text
7297  * @cfg {String} cls cell class
7298  * @cfg {String} tag cell tag (td|th) default td
7299  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7300  * @cfg {String} align Aligns the content in a cell
7301  * @cfg {String} axis Categorizes cells
7302  * @cfg {String} bgcolor Specifies the background color of a cell
7303  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7304  * @cfg {Number} colspan Specifies the number of columns a cell should span
7305  * @cfg {String} headers Specifies one or more header cells a cell is related to
7306  * @cfg {Number} height Sets the height of a cell
7307  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7308  * @cfg {Number} rowspan Sets the number of rows a cell should span
7309  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7310  * @cfg {String} valign Vertical aligns the content in a cell
7311  * @cfg {Number} width Specifies the width of a cell
7312  * 
7313  * @constructor
7314  * Create a new TableCell
7315  * @param {Object} config The config object
7316  */
7317
7318 Roo.bootstrap.TableCell = function(config){
7319     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7320 };
7321
7322 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7323     
7324     html: false,
7325     cls: false,
7326     tag: false,
7327     abbr: false,
7328     align: false,
7329     axis: false,
7330     bgcolor: false,
7331     charoff: false,
7332     colspan: false,
7333     headers: false,
7334     height: false,
7335     nowrap: false,
7336     rowspan: false,
7337     scope: false,
7338     valign: false,
7339     width: false,
7340     
7341     
7342     getAutoCreate : function(){
7343         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7344         
7345         cfg = {
7346             tag: 'td'
7347         };
7348         
7349         if(this.tag){
7350             cfg.tag = this.tag;
7351         }
7352         
7353         if (this.html) {
7354             cfg.html=this.html
7355         }
7356         if (this.cls) {
7357             cfg.cls=this.cls
7358         }
7359         if (this.abbr) {
7360             cfg.abbr=this.abbr
7361         }
7362         if (this.align) {
7363             cfg.align=this.align
7364         }
7365         if (this.axis) {
7366             cfg.axis=this.axis
7367         }
7368         if (this.bgcolor) {
7369             cfg.bgcolor=this.bgcolor
7370         }
7371         if (this.charoff) {
7372             cfg.charoff=this.charoff
7373         }
7374         if (this.colspan) {
7375             cfg.colspan=this.colspan
7376         }
7377         if (this.headers) {
7378             cfg.headers=this.headers
7379         }
7380         if (this.height) {
7381             cfg.height=this.height
7382         }
7383         if (this.nowrap) {
7384             cfg.nowrap=this.nowrap
7385         }
7386         if (this.rowspan) {
7387             cfg.rowspan=this.rowspan
7388         }
7389         if (this.scope) {
7390             cfg.scope=this.scope
7391         }
7392         if (this.valign) {
7393             cfg.valign=this.valign
7394         }
7395         if (this.width) {
7396             cfg.width=this.width
7397         }
7398         
7399         
7400         return cfg;
7401     }
7402    
7403 });
7404
7405  
7406
7407  /*
7408  * - LGPL
7409  *
7410  * table row
7411  * 
7412  */
7413
7414 /**
7415  * @class Roo.bootstrap.TableRow
7416  * @extends Roo.bootstrap.Component
7417  * Bootstrap TableRow class
7418  * @cfg {String} cls row class
7419  * @cfg {String} align Aligns the content in a table row
7420  * @cfg {String} bgcolor Specifies a background color for a table row
7421  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7422  * @cfg {String} valign Vertical aligns the content in a table row
7423  * 
7424  * @constructor
7425  * Create a new TableRow
7426  * @param {Object} config The config object
7427  */
7428
7429 Roo.bootstrap.TableRow = function(config){
7430     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7431 };
7432
7433 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7434     
7435     cls: false,
7436     align: false,
7437     bgcolor: false,
7438     charoff: false,
7439     valign: false,
7440     
7441     getAutoCreate : function(){
7442         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7443         
7444         cfg = {
7445             tag: 'tr'
7446         };
7447             
7448         if(this.cls){
7449             cfg.cls = this.cls;
7450         }
7451         if(this.align){
7452             cfg.align = this.align;
7453         }
7454         if(this.bgcolor){
7455             cfg.bgcolor = this.bgcolor;
7456         }
7457         if(this.charoff){
7458             cfg.charoff = this.charoff;
7459         }
7460         if(this.valign){
7461             cfg.valign = this.valign;
7462         }
7463         
7464         return cfg;
7465     }
7466    
7467 });
7468
7469  
7470
7471  /*
7472  * - LGPL
7473  *
7474  * table body
7475  * 
7476  */
7477
7478 /**
7479  * @class Roo.bootstrap.TableBody
7480  * @extends Roo.bootstrap.Component
7481  * Bootstrap TableBody class
7482  * @cfg {String} cls element class
7483  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7484  * @cfg {String} align Aligns the content inside the element
7485  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7486  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7487  * 
7488  * @constructor
7489  * Create a new TableBody
7490  * @param {Object} config The config object
7491  */
7492
7493 Roo.bootstrap.TableBody = function(config){
7494     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7495 };
7496
7497 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7498     
7499     cls: false,
7500     tag: false,
7501     align: false,
7502     charoff: false,
7503     valign: false,
7504     
7505     getAutoCreate : function(){
7506         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7507         
7508         cfg = {
7509             tag: 'tbody'
7510         };
7511             
7512         if (this.cls) {
7513             cfg.cls=this.cls
7514         }
7515         if(this.tag){
7516             cfg.tag = this.tag;
7517         }
7518         
7519         if(this.align){
7520             cfg.align = this.align;
7521         }
7522         if(this.charoff){
7523             cfg.charoff = this.charoff;
7524         }
7525         if(this.valign){
7526             cfg.valign = this.valign;
7527         }
7528         
7529         return cfg;
7530     }
7531     
7532     
7533 //    initEvents : function()
7534 //    {
7535 //        
7536 //        if(!this.store){
7537 //            return;
7538 //        }
7539 //        
7540 //        this.store = Roo.factory(this.store, Roo.data);
7541 //        this.store.on('load', this.onLoad, this);
7542 //        
7543 //        this.store.load();
7544 //        
7545 //    },
7546 //    
7547 //    onLoad: function () 
7548 //    {   
7549 //        this.fireEvent('load', this);
7550 //    }
7551 //    
7552 //   
7553 });
7554
7555  
7556
7557  /*
7558  * Based on:
7559  * Ext JS Library 1.1.1
7560  * Copyright(c) 2006-2007, Ext JS, LLC.
7561  *
7562  * Originally Released Under LGPL - original licence link has changed is not relivant.
7563  *
7564  * Fork - LGPL
7565  * <script type="text/javascript">
7566  */
7567
7568 // as we use this in bootstrap.
7569 Roo.namespace('Roo.form');
7570  /**
7571  * @class Roo.form.Action
7572  * Internal Class used to handle form actions
7573  * @constructor
7574  * @param {Roo.form.BasicForm} el The form element or its id
7575  * @param {Object} config Configuration options
7576  */
7577
7578  
7579  
7580 // define the action interface
7581 Roo.form.Action = function(form, options){
7582     this.form = form;
7583     this.options = options || {};
7584 };
7585 /**
7586  * Client Validation Failed
7587  * @const 
7588  */
7589 Roo.form.Action.CLIENT_INVALID = 'client';
7590 /**
7591  * Server Validation Failed
7592  * @const 
7593  */
7594 Roo.form.Action.SERVER_INVALID = 'server';
7595  /**
7596  * Connect to Server Failed
7597  * @const 
7598  */
7599 Roo.form.Action.CONNECT_FAILURE = 'connect';
7600 /**
7601  * Reading Data from Server Failed
7602  * @const 
7603  */
7604 Roo.form.Action.LOAD_FAILURE = 'load';
7605
7606 Roo.form.Action.prototype = {
7607     type : 'default',
7608     failureType : undefined,
7609     response : undefined,
7610     result : undefined,
7611
7612     // interface method
7613     run : function(options){
7614
7615     },
7616
7617     // interface method
7618     success : function(response){
7619
7620     },
7621
7622     // interface method
7623     handleResponse : function(response){
7624
7625     },
7626
7627     // default connection failure
7628     failure : function(response){
7629         
7630         this.response = response;
7631         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7632         this.form.afterAction(this, false);
7633     },
7634
7635     processResponse : function(response){
7636         this.response = response;
7637         if(!response.responseText){
7638             return true;
7639         }
7640         this.result = this.handleResponse(response);
7641         return this.result;
7642     },
7643
7644     // utility functions used internally
7645     getUrl : function(appendParams){
7646         var url = this.options.url || this.form.url || this.form.el.dom.action;
7647         if(appendParams){
7648             var p = this.getParams();
7649             if(p){
7650                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7651             }
7652         }
7653         return url;
7654     },
7655
7656     getMethod : function(){
7657         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7658     },
7659
7660     getParams : function(){
7661         var bp = this.form.baseParams;
7662         var p = this.options.params;
7663         if(p){
7664             if(typeof p == "object"){
7665                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7666             }else if(typeof p == 'string' && bp){
7667                 p += '&' + Roo.urlEncode(bp);
7668             }
7669         }else if(bp){
7670             p = Roo.urlEncode(bp);
7671         }
7672         return p;
7673     },
7674
7675     createCallback : function(){
7676         return {
7677             success: this.success,
7678             failure: this.failure,
7679             scope: this,
7680             timeout: (this.form.timeout*1000),
7681             upload: this.form.fileUpload ? this.success : undefined
7682         };
7683     }
7684 };
7685
7686 Roo.form.Action.Submit = function(form, options){
7687     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7688 };
7689
7690 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7691     type : 'submit',
7692
7693     haveProgress : false,
7694     uploadComplete : false,
7695     
7696     // uploadProgress indicator.
7697     uploadProgress : function()
7698     {
7699         if (!this.form.progressUrl) {
7700             return;
7701         }
7702         
7703         if (!this.haveProgress) {
7704             Roo.MessageBox.progress("Uploading", "Uploading");
7705         }
7706         if (this.uploadComplete) {
7707            Roo.MessageBox.hide();
7708            return;
7709         }
7710         
7711         this.haveProgress = true;
7712    
7713         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7714         
7715         var c = new Roo.data.Connection();
7716         c.request({
7717             url : this.form.progressUrl,
7718             params: {
7719                 id : uid
7720             },
7721             method: 'GET',
7722             success : function(req){
7723                //console.log(data);
7724                 var rdata = false;
7725                 var edata;
7726                 try  {
7727                    rdata = Roo.decode(req.responseText)
7728                 } catch (e) {
7729                     Roo.log("Invalid data from server..");
7730                     Roo.log(edata);
7731                     return;
7732                 }
7733                 if (!rdata || !rdata.success) {
7734                     Roo.log(rdata);
7735                     Roo.MessageBox.alert(Roo.encode(rdata));
7736                     return;
7737                 }
7738                 var data = rdata.data;
7739                 
7740                 if (this.uploadComplete) {
7741                    Roo.MessageBox.hide();
7742                    return;
7743                 }
7744                    
7745                 if (data){
7746                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7747                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7748                     );
7749                 }
7750                 this.uploadProgress.defer(2000,this);
7751             },
7752        
7753             failure: function(data) {
7754                 Roo.log('progress url failed ');
7755                 Roo.log(data);
7756             },
7757             scope : this
7758         });
7759            
7760     },
7761     
7762     
7763     run : function()
7764     {
7765         // run get Values on the form, so it syncs any secondary forms.
7766         this.form.getValues();
7767         
7768         var o = this.options;
7769         var method = this.getMethod();
7770         var isPost = method == 'POST';
7771         if(o.clientValidation === false || this.form.isValid()){
7772             
7773             if (this.form.progressUrl) {
7774                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7775                     (new Date() * 1) + '' + Math.random());
7776                     
7777             } 
7778             
7779             
7780             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7781                 form:this.form.el.dom,
7782                 url:this.getUrl(!isPost),
7783                 method: method,
7784                 params:isPost ? this.getParams() : null,
7785                 isUpload: this.form.fileUpload
7786             }));
7787             
7788             this.uploadProgress();
7789
7790         }else if (o.clientValidation !== false){ // client validation failed
7791             this.failureType = Roo.form.Action.CLIENT_INVALID;
7792             this.form.afterAction(this, false);
7793         }
7794     },
7795
7796     success : function(response)
7797     {
7798         this.uploadComplete= true;
7799         if (this.haveProgress) {
7800             Roo.MessageBox.hide();
7801         }
7802         
7803         
7804         var result = this.processResponse(response);
7805         if(result === true || result.success){
7806             this.form.afterAction(this, true);
7807             return;
7808         }
7809         if(result.errors){
7810             this.form.markInvalid(result.errors);
7811             this.failureType = Roo.form.Action.SERVER_INVALID;
7812         }
7813         this.form.afterAction(this, false);
7814     },
7815     failure : function(response)
7816     {
7817         this.uploadComplete= true;
7818         if (this.haveProgress) {
7819             Roo.MessageBox.hide();
7820         }
7821         
7822         this.response = response;
7823         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7824         this.form.afterAction(this, false);
7825     },
7826     
7827     handleResponse : function(response){
7828         if(this.form.errorReader){
7829             var rs = this.form.errorReader.read(response);
7830             var errors = [];
7831             if(rs.records){
7832                 for(var i = 0, len = rs.records.length; i < len; i++) {
7833                     var r = rs.records[i];
7834                     errors[i] = r.data;
7835                 }
7836             }
7837             if(errors.length < 1){
7838                 errors = null;
7839             }
7840             return {
7841                 success : rs.success,
7842                 errors : errors
7843             };
7844         }
7845         var ret = false;
7846         try {
7847             ret = Roo.decode(response.responseText);
7848         } catch (e) {
7849             ret = {
7850                 success: false,
7851                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7852                 errors : []
7853             };
7854         }
7855         return ret;
7856         
7857     }
7858 });
7859
7860
7861 Roo.form.Action.Load = function(form, options){
7862     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7863     this.reader = this.form.reader;
7864 };
7865
7866 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7867     type : 'load',
7868
7869     run : function(){
7870         
7871         Roo.Ajax.request(Roo.apply(
7872                 this.createCallback(), {
7873                     method:this.getMethod(),
7874                     url:this.getUrl(false),
7875                     params:this.getParams()
7876         }));
7877     },
7878
7879     success : function(response){
7880         
7881         var result = this.processResponse(response);
7882         if(result === true || !result.success || !result.data){
7883             this.failureType = Roo.form.Action.LOAD_FAILURE;
7884             this.form.afterAction(this, false);
7885             return;
7886         }
7887         this.form.clearInvalid();
7888         this.form.setValues(result.data);
7889         this.form.afterAction(this, true);
7890     },
7891
7892     handleResponse : function(response){
7893         if(this.form.reader){
7894             var rs = this.form.reader.read(response);
7895             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7896             return {
7897                 success : rs.success,
7898                 data : data
7899             };
7900         }
7901         return Roo.decode(response.responseText);
7902     }
7903 });
7904
7905 Roo.form.Action.ACTION_TYPES = {
7906     'load' : Roo.form.Action.Load,
7907     'submit' : Roo.form.Action.Submit
7908 };/*
7909  * - LGPL
7910  *
7911  * form
7912  *
7913  */
7914
7915 /**
7916  * @class Roo.bootstrap.Form
7917  * @extends Roo.bootstrap.Component
7918  * Bootstrap Form class
7919  * @cfg {String} method  GET | POST (default POST)
7920  * @cfg {String} labelAlign top | left (default top)
7921  * @cfg {String} align left  | right - for navbars
7922  * @cfg {Boolean} loadMask load mask when submit (default true)
7923
7924  *
7925  * @constructor
7926  * Create a new Form
7927  * @param {Object} config The config object
7928  */
7929
7930
7931 Roo.bootstrap.Form = function(config){
7932     
7933     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7934     
7935     Roo.bootstrap.Form.popover.apply();
7936     
7937     this.addEvents({
7938         /**
7939          * @event clientvalidation
7940          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7941          * @param {Form} this
7942          * @param {Boolean} valid true if the form has passed client-side validation
7943          */
7944         clientvalidation: true,
7945         /**
7946          * @event beforeaction
7947          * Fires before any action is performed. Return false to cancel the action.
7948          * @param {Form} this
7949          * @param {Action} action The action to be performed
7950          */
7951         beforeaction: true,
7952         /**
7953          * @event actionfailed
7954          * Fires when an action fails.
7955          * @param {Form} this
7956          * @param {Action} action The action that failed
7957          */
7958         actionfailed : true,
7959         /**
7960          * @event actioncomplete
7961          * Fires when an action is completed.
7962          * @param {Form} this
7963          * @param {Action} action The action that completed
7964          */
7965         actioncomplete : true
7966     });
7967 };
7968
7969 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7970
7971      /**
7972      * @cfg {String} method
7973      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7974      */
7975     method : 'POST',
7976     /**
7977      * @cfg {String} url
7978      * The URL to use for form actions if one isn't supplied in the action options.
7979      */
7980     /**
7981      * @cfg {Boolean} fileUpload
7982      * Set to true if this form is a file upload.
7983      */
7984
7985     /**
7986      * @cfg {Object} baseParams
7987      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7988      */
7989
7990     /**
7991      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7992      */
7993     timeout: 30,
7994     /**
7995      * @cfg {Sting} align (left|right) for navbar forms
7996      */
7997     align : 'left',
7998
7999     // private
8000     activeAction : null,
8001
8002     /**
8003      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8004      * element by passing it or its id or mask the form itself by passing in true.
8005      * @type Mixed
8006      */
8007     waitMsgTarget : false,
8008
8009     loadMask : true,
8010     
8011     /**
8012      * @cfg {Boolean} errorMask (true|false) default false
8013      */
8014     errorMask : false,
8015     
8016     /**
8017      * @cfg {Number} maskOffset Default 100
8018      */
8019     maskOffset : 100,
8020     
8021     /**
8022      * @cfg {Boolean} maskBody
8023      */
8024     maskBody : false,
8025
8026     getAutoCreate : function(){
8027
8028         var cfg = {
8029             tag: 'form',
8030             method : this.method || 'POST',
8031             id : this.id || Roo.id(),
8032             cls : ''
8033         };
8034         if (this.parent().xtype.match(/^Nav/)) {
8035             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8036
8037         }
8038
8039         if (this.labelAlign == 'left' ) {
8040             cfg.cls += ' form-horizontal';
8041         }
8042
8043
8044         return cfg;
8045     },
8046     initEvents : function()
8047     {
8048         this.el.on('submit', this.onSubmit, this);
8049         // this was added as random key presses on the form where triggering form submit.
8050         this.el.on('keypress', function(e) {
8051             if (e.getCharCode() != 13) {
8052                 return true;
8053             }
8054             // we might need to allow it for textareas.. and some other items.
8055             // check e.getTarget().
8056
8057             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8058                 return true;
8059             }
8060
8061             Roo.log("keypress blocked");
8062
8063             e.preventDefault();
8064             return false;
8065         });
8066         
8067     },
8068     // private
8069     onSubmit : function(e){
8070         e.stopEvent();
8071     },
8072
8073      /**
8074      * Returns true if client-side validation on the form is successful.
8075      * @return Boolean
8076      */
8077     isValid : function(){
8078         var items = this.getItems();
8079         var valid = true;
8080         var target = false;
8081         
8082         items.each(function(f){
8083             
8084             if(f.validate()){
8085                 return;
8086             }
8087             
8088             Roo.log('invalid field: ' + f.name);
8089             
8090             valid = false;
8091
8092             if(!target && f.el.isVisible(true)){
8093                 target = f;
8094             }
8095            
8096         });
8097         
8098         if(this.errorMask && !valid){
8099             Roo.bootstrap.Form.popover.mask(this, target);
8100         }
8101         
8102         return valid;
8103     },
8104     
8105     /**
8106      * Returns true if any fields in this form have changed since their original load.
8107      * @return Boolean
8108      */
8109     isDirty : function(){
8110         var dirty = false;
8111         var items = this.getItems();
8112         items.each(function(f){
8113            if(f.isDirty()){
8114                dirty = true;
8115                return false;
8116            }
8117            return true;
8118         });
8119         return dirty;
8120     },
8121      /**
8122      * Performs a predefined action (submit or load) or custom actions you define on this form.
8123      * @param {String} actionName The name of the action type
8124      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8125      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8126      * accept other config options):
8127      * <pre>
8128 Property          Type             Description
8129 ----------------  ---------------  ----------------------------------------------------------------------------------
8130 url               String           The url for the action (defaults to the form's url)
8131 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8132 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8133 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8134                                    validate the form on the client (defaults to false)
8135      * </pre>
8136      * @return {BasicForm} this
8137      */
8138     doAction : function(action, options){
8139         if(typeof action == 'string'){
8140             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8141         }
8142         if(this.fireEvent('beforeaction', this, action) !== false){
8143             this.beforeAction(action);
8144             action.run.defer(100, action);
8145         }
8146         return this;
8147     },
8148
8149     // private
8150     beforeAction : function(action){
8151         var o = action.options;
8152         
8153         if(this.loadMask){
8154             
8155             if(this.maskBody){
8156                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8157             } else {
8158                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8159             }
8160         }
8161         // not really supported yet.. ??
8162
8163         //if(this.waitMsgTarget === true){
8164         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8165         //}else if(this.waitMsgTarget){
8166         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8167         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8168         //}else {
8169         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8170        // }
8171
8172     },
8173
8174     // private
8175     afterAction : function(action, success){
8176         this.activeAction = null;
8177         var o = action.options;
8178
8179         if(this.loadMask){
8180             
8181             if(this.maskBody){
8182                 Roo.get(document.body).unmask();
8183             } else {
8184                 this.el.unmask();
8185             }
8186         }
8187         
8188         //if(this.waitMsgTarget === true){
8189 //            this.el.unmask();
8190         //}else if(this.waitMsgTarget){
8191         //    this.waitMsgTarget.unmask();
8192         //}else{
8193         //    Roo.MessageBox.updateProgress(1);
8194         //    Roo.MessageBox.hide();
8195        // }
8196         //
8197         if(success){
8198             if(o.reset){
8199                 this.reset();
8200             }
8201             Roo.callback(o.success, o.scope, [this, action]);
8202             this.fireEvent('actioncomplete', this, action);
8203
8204         }else{
8205
8206             // failure condition..
8207             // we have a scenario where updates need confirming.
8208             // eg. if a locking scenario exists..
8209             // we look for { errors : { needs_confirm : true }} in the response.
8210             if (
8211                 (typeof(action.result) != 'undefined')  &&
8212                 (typeof(action.result.errors) != 'undefined')  &&
8213                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8214            ){
8215                 var _t = this;
8216                 Roo.log("not supported yet");
8217                  /*
8218
8219                 Roo.MessageBox.confirm(
8220                     "Change requires confirmation",
8221                     action.result.errorMsg,
8222                     function(r) {
8223                         if (r != 'yes') {
8224                             return;
8225                         }
8226                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8227                     }
8228
8229                 );
8230                 */
8231
8232
8233                 return;
8234             }
8235
8236             Roo.callback(o.failure, o.scope, [this, action]);
8237             // show an error message if no failed handler is set..
8238             if (!this.hasListener('actionfailed')) {
8239                 Roo.log("need to add dialog support");
8240                 /*
8241                 Roo.MessageBox.alert("Error",
8242                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8243                         action.result.errorMsg :
8244                         "Saving Failed, please check your entries or try again"
8245                 );
8246                 */
8247             }
8248
8249             this.fireEvent('actionfailed', this, action);
8250         }
8251
8252     },
8253     /**
8254      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8255      * @param {String} id The value to search for
8256      * @return Field
8257      */
8258     findField : function(id){
8259         var items = this.getItems();
8260         var field = items.get(id);
8261         if(!field){
8262              items.each(function(f){
8263                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8264                     field = f;
8265                     return false;
8266                 }
8267                 return true;
8268             });
8269         }
8270         return field || null;
8271     },
8272      /**
8273      * Mark fields in this form invalid in bulk.
8274      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8275      * @return {BasicForm} this
8276      */
8277     markInvalid : function(errors){
8278         if(errors instanceof Array){
8279             for(var i = 0, len = errors.length; i < len; i++){
8280                 var fieldError = errors[i];
8281                 var f = this.findField(fieldError.id);
8282                 if(f){
8283                     f.markInvalid(fieldError.msg);
8284                 }
8285             }
8286         }else{
8287             var field, id;
8288             for(id in errors){
8289                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8290                     field.markInvalid(errors[id]);
8291                 }
8292             }
8293         }
8294         //Roo.each(this.childForms || [], function (f) {
8295         //    f.markInvalid(errors);
8296         //});
8297
8298         return this;
8299     },
8300
8301     /**
8302      * Set values for fields in this form in bulk.
8303      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8304      * @return {BasicForm} this
8305      */
8306     setValues : function(values){
8307         if(values instanceof Array){ // array of objects
8308             for(var i = 0, len = values.length; i < len; i++){
8309                 var v = values[i];
8310                 var f = this.findField(v.id);
8311                 if(f){
8312                     f.setValue(v.value);
8313                     if(this.trackResetOnLoad){
8314                         f.originalValue = f.getValue();
8315                     }
8316                 }
8317             }
8318         }else{ // object hash
8319             var field, id;
8320             for(id in values){
8321                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8322
8323                     if (field.setFromData &&
8324                         field.valueField &&
8325                         field.displayField &&
8326                         // combos' with local stores can
8327                         // be queried via setValue()
8328                         // to set their value..
8329                         (field.store && !field.store.isLocal)
8330                         ) {
8331                         // it's a combo
8332                         var sd = { };
8333                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8334                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8335                         field.setFromData(sd);
8336
8337                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8338                         
8339                         field.setFromData(values);
8340                         
8341                     } else {
8342                         field.setValue(values[id]);
8343                     }
8344
8345
8346                     if(this.trackResetOnLoad){
8347                         field.originalValue = field.getValue();
8348                     }
8349                 }
8350             }
8351         }
8352
8353         //Roo.each(this.childForms || [], function (f) {
8354         //    f.setValues(values);
8355         //});
8356
8357         return this;
8358     },
8359
8360     /**
8361      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8362      * they are returned as an array.
8363      * @param {Boolean} asString
8364      * @return {Object}
8365      */
8366     getValues : function(asString){
8367         //if (this.childForms) {
8368             // copy values from the child forms
8369         //    Roo.each(this.childForms, function (f) {
8370         //        this.setValues(f.getValues());
8371         //    }, this);
8372         //}
8373
8374
8375
8376         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8377         if(asString === true){
8378             return fs;
8379         }
8380         return Roo.urlDecode(fs);
8381     },
8382
8383     /**
8384      * Returns the fields in this form as an object with key/value pairs.
8385      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8386      * @return {Object}
8387      */
8388     getFieldValues : function(with_hidden)
8389     {
8390         var items = this.getItems();
8391         var ret = {};
8392         items.each(function(f){
8393             
8394             if (!f.getName()) {
8395                 return;
8396             }
8397             
8398             var v = f.getValue();
8399             
8400             if (f.inputType =='radio') {
8401                 if (typeof(ret[f.getName()]) == 'undefined') {
8402                     ret[f.getName()] = ''; // empty..
8403                 }
8404
8405                 if (!f.el.dom.checked) {
8406                     return;
8407
8408                 }
8409                 v = f.el.dom.value;
8410
8411             }
8412             
8413             if(f.xtype == 'MoneyField'){
8414                 ret[f.currencyName] = f.getCurrency();
8415             }
8416
8417             // not sure if this supported any more..
8418             if ((typeof(v) == 'object') && f.getRawValue) {
8419                 v = f.getRawValue() ; // dates..
8420             }
8421             // combo boxes where name != hiddenName...
8422             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8423                 ret[f.name] = f.getRawValue();
8424             }
8425             ret[f.getName()] = v;
8426         });
8427
8428         return ret;
8429     },
8430
8431     /**
8432      * Clears all invalid messages in this form.
8433      * @return {BasicForm} this
8434      */
8435     clearInvalid : function(){
8436         var items = this.getItems();
8437
8438         items.each(function(f){
8439            f.clearInvalid();
8440         });
8441
8442         return this;
8443     },
8444
8445     /**
8446      * Resets this form.
8447      * @return {BasicForm} this
8448      */
8449     reset : function(){
8450         var items = this.getItems();
8451         items.each(function(f){
8452             f.reset();
8453         });
8454
8455         Roo.each(this.childForms || [], function (f) {
8456             f.reset();
8457         });
8458
8459
8460         return this;
8461     },
8462     
8463     getItems : function()
8464     {
8465         var r=new Roo.util.MixedCollection(false, function(o){
8466             return o.id || (o.id = Roo.id());
8467         });
8468         var iter = function(el) {
8469             if (el.inputEl) {
8470                 r.add(el);
8471             }
8472             if (!el.items) {
8473                 return;
8474             }
8475             Roo.each(el.items,function(e) {
8476                 iter(e);
8477             });
8478         };
8479
8480         iter(this);
8481         return r;
8482     },
8483     
8484     hideFields : function(items)
8485     {
8486         Roo.each(items, function(i){
8487             
8488             var f = this.findField(i);
8489             
8490             if(!f){
8491                 return;
8492             }
8493             
8494             f.hide();
8495             
8496         }, this);
8497     },
8498     
8499     showFields : function(items)
8500     {
8501         Roo.each(items, function(i){
8502             
8503             var f = this.findField(i);
8504             
8505             if(!f){
8506                 return;
8507             }
8508             
8509             f.show();
8510             
8511         }, this);
8512     }
8513
8514 });
8515
8516 Roo.apply(Roo.bootstrap.Form, {
8517     
8518     popover : {
8519         
8520         padding : 5,
8521         
8522         isApplied : false,
8523         
8524         isMasked : false,
8525         
8526         form : false,
8527         
8528         target : false,
8529         
8530         toolTip : false,
8531         
8532         intervalID : false,
8533         
8534         maskEl : false,
8535         
8536         apply : function()
8537         {
8538             if(this.isApplied){
8539                 return;
8540             }
8541             
8542             this.maskEl = {
8543                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8544                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8545                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8546                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8547             };
8548             
8549             this.maskEl.top.enableDisplayMode("block");
8550             this.maskEl.left.enableDisplayMode("block");
8551             this.maskEl.bottom.enableDisplayMode("block");
8552             this.maskEl.right.enableDisplayMode("block");
8553             
8554             this.toolTip = new Roo.bootstrap.Tooltip({
8555                 cls : 'roo-form-error-popover',
8556                 alignment : {
8557                     'left' : ['r-l', [-2,0], 'right'],
8558                     'right' : ['l-r', [2,0], 'left'],
8559                     'bottom' : ['tl-bl', [0,2], 'top'],
8560                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8561                 }
8562             });
8563             
8564             this.toolTip.render(Roo.get(document.body));
8565
8566             this.toolTip.el.enableDisplayMode("block");
8567             
8568             Roo.get(document.body).on('click', function(){
8569                 this.unmask();
8570             }, this);
8571             
8572             Roo.get(document.body).on('touchstart', function(){
8573                 this.unmask();
8574             }, this);
8575             
8576             this.isApplied = true
8577         },
8578         
8579         mask : function(form, target)
8580         {
8581             this.form = form;
8582             
8583             this.target = target;
8584             
8585             if(!this.form.errorMask || !target.el){
8586                 return;
8587             }
8588             
8589             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8590             
8591             Roo.log(scrollable);
8592             
8593             var ot = this.target.el.calcOffsetsTo(scrollable);
8594             
8595             var scrollTo = ot[1] - this.form.maskOffset;
8596             
8597             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8598             
8599             scrollable.scrollTo('top', scrollTo);
8600             
8601             var box = this.target.el.getBox();
8602             Roo.log(box);
8603             var zIndex = Roo.bootstrap.Modal.zIndex++;
8604
8605             
8606             this.maskEl.top.setStyle('position', 'absolute');
8607             this.maskEl.top.setStyle('z-index', zIndex);
8608             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8609             this.maskEl.top.setLeft(0);
8610             this.maskEl.top.setTop(0);
8611             this.maskEl.top.show();
8612             
8613             this.maskEl.left.setStyle('position', 'absolute');
8614             this.maskEl.left.setStyle('z-index', zIndex);
8615             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8616             this.maskEl.left.setLeft(0);
8617             this.maskEl.left.setTop(box.y - this.padding);
8618             this.maskEl.left.show();
8619
8620             this.maskEl.bottom.setStyle('position', 'absolute');
8621             this.maskEl.bottom.setStyle('z-index', zIndex);
8622             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8623             this.maskEl.bottom.setLeft(0);
8624             this.maskEl.bottom.setTop(box.bottom + this.padding);
8625             this.maskEl.bottom.show();
8626
8627             this.maskEl.right.setStyle('position', 'absolute');
8628             this.maskEl.right.setStyle('z-index', zIndex);
8629             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8630             this.maskEl.right.setLeft(box.right + this.padding);
8631             this.maskEl.right.setTop(box.y - this.padding);
8632             this.maskEl.right.show();
8633
8634             this.toolTip.bindEl = this.target.el;
8635
8636             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8637
8638             var tip = this.target.blankText;
8639
8640             if(this.target.getValue() !== '' ) {
8641                 
8642                 if (this.target.invalidText.length) {
8643                     tip = this.target.invalidText;
8644                 } else if (this.target.regexText.length){
8645                     tip = this.target.regexText;
8646                 }
8647             }
8648
8649             this.toolTip.show(tip);
8650
8651             this.intervalID = window.setInterval(function() {
8652                 Roo.bootstrap.Form.popover.unmask();
8653             }, 10000);
8654
8655             window.onwheel = function(){ return false;};
8656             
8657             (function(){ this.isMasked = true; }).defer(500, this);
8658             
8659         },
8660         
8661         unmask : function()
8662         {
8663             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8664                 return;
8665             }
8666             
8667             this.maskEl.top.setStyle('position', 'absolute');
8668             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8669             this.maskEl.top.hide();
8670
8671             this.maskEl.left.setStyle('position', 'absolute');
8672             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8673             this.maskEl.left.hide();
8674
8675             this.maskEl.bottom.setStyle('position', 'absolute');
8676             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8677             this.maskEl.bottom.hide();
8678
8679             this.maskEl.right.setStyle('position', 'absolute');
8680             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8681             this.maskEl.right.hide();
8682             
8683             this.toolTip.hide();
8684             
8685             this.toolTip.el.hide();
8686             
8687             window.onwheel = function(){ return true;};
8688             
8689             if(this.intervalID){
8690                 window.clearInterval(this.intervalID);
8691                 this.intervalID = false;
8692             }
8693             
8694             this.isMasked = false;
8695             
8696         }
8697         
8698     }
8699     
8700 });
8701
8702 /*
8703  * Based on:
8704  * Ext JS Library 1.1.1
8705  * Copyright(c) 2006-2007, Ext JS, LLC.
8706  *
8707  * Originally Released Under LGPL - original licence link has changed is not relivant.
8708  *
8709  * Fork - LGPL
8710  * <script type="text/javascript">
8711  */
8712 /**
8713  * @class Roo.form.VTypes
8714  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8715  * @singleton
8716  */
8717 Roo.form.VTypes = function(){
8718     // closure these in so they are only created once.
8719     var alpha = /^[a-zA-Z_]+$/;
8720     var alphanum = /^[a-zA-Z0-9_]+$/;
8721     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8722     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8723
8724     // All these messages and functions are configurable
8725     return {
8726         /**
8727          * The function used to validate email addresses
8728          * @param {String} value The email address
8729          */
8730         'email' : function(v){
8731             return email.test(v);
8732         },
8733         /**
8734          * The error text to display when the email validation function returns false
8735          * @type String
8736          */
8737         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8738         /**
8739          * The keystroke filter mask to be applied on email input
8740          * @type RegExp
8741          */
8742         'emailMask' : /[a-z0-9_\.\-@]/i,
8743
8744         /**
8745          * The function used to validate URLs
8746          * @param {String} value The URL
8747          */
8748         'url' : function(v){
8749             return url.test(v);
8750         },
8751         /**
8752          * The error text to display when the url validation function returns false
8753          * @type String
8754          */
8755         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8756         
8757         /**
8758          * The function used to validate alpha values
8759          * @param {String} value The value
8760          */
8761         'alpha' : function(v){
8762             return alpha.test(v);
8763         },
8764         /**
8765          * The error text to display when the alpha validation function returns false
8766          * @type String
8767          */
8768         'alphaText' : 'This field should only contain letters and _',
8769         /**
8770          * The keystroke filter mask to be applied on alpha input
8771          * @type RegExp
8772          */
8773         'alphaMask' : /[a-z_]/i,
8774
8775         /**
8776          * The function used to validate alphanumeric values
8777          * @param {String} value The value
8778          */
8779         'alphanum' : function(v){
8780             return alphanum.test(v);
8781         },
8782         /**
8783          * The error text to display when the alphanumeric validation function returns false
8784          * @type String
8785          */
8786         'alphanumText' : 'This field should only contain letters, numbers and _',
8787         /**
8788          * The keystroke filter mask to be applied on alphanumeric input
8789          * @type RegExp
8790          */
8791         'alphanumMask' : /[a-z0-9_]/i
8792     };
8793 }();/*
8794  * - LGPL
8795  *
8796  * Input
8797  * 
8798  */
8799
8800 /**
8801  * @class Roo.bootstrap.Input
8802  * @extends Roo.bootstrap.Component
8803  * Bootstrap Input class
8804  * @cfg {Boolean} disabled is it disabled
8805  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8806  * @cfg {String} name name of the input
8807  * @cfg {string} fieldLabel - the label associated
8808  * @cfg {string} placeholder - placeholder to put in text.
8809  * @cfg {string}  before - input group add on before
8810  * @cfg {string} after - input group add on after
8811  * @cfg {string} size - (lg|sm) or leave empty..
8812  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8813  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8814  * @cfg {Number} md colspan out of 12 for computer-sized screens
8815  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8816  * @cfg {string} value default value of the input
8817  * @cfg {Number} labelWidth set the width of label 
8818  * @cfg {Number} labellg set the width of label (1-12)
8819  * @cfg {Number} labelmd set the width of label (1-12)
8820  * @cfg {Number} labelsm set the width of label (1-12)
8821  * @cfg {Number} labelxs set the width of label (1-12)
8822  * @cfg {String} labelAlign (top|left)
8823  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8824  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8825  * @cfg {String} indicatorpos (left|right) default left
8826  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8827  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8828
8829  * @cfg {String} align (left|center|right) Default left
8830  * @cfg {Boolean} forceFeedback (true|false) Default false
8831  * 
8832  * @constructor
8833  * Create a new Input
8834  * @param {Object} config The config object
8835  */
8836
8837 Roo.bootstrap.Input = function(config){
8838     
8839     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8840     
8841     this.addEvents({
8842         /**
8843          * @event focus
8844          * Fires when this field receives input focus.
8845          * @param {Roo.form.Field} this
8846          */
8847         focus : true,
8848         /**
8849          * @event blur
8850          * Fires when this field loses input focus.
8851          * @param {Roo.form.Field} this
8852          */
8853         blur : true,
8854         /**
8855          * @event specialkey
8856          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8857          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8858          * @param {Roo.form.Field} this
8859          * @param {Roo.EventObject} e The event object
8860          */
8861         specialkey : true,
8862         /**
8863          * @event change
8864          * Fires just before the field blurs if the field value has changed.
8865          * @param {Roo.form.Field} this
8866          * @param {Mixed} newValue The new value
8867          * @param {Mixed} oldValue The original value
8868          */
8869         change : true,
8870         /**
8871          * @event invalid
8872          * Fires after the field has been marked as invalid.
8873          * @param {Roo.form.Field} this
8874          * @param {String} msg The validation message
8875          */
8876         invalid : true,
8877         /**
8878          * @event valid
8879          * Fires after the field has been validated with no errors.
8880          * @param {Roo.form.Field} this
8881          */
8882         valid : true,
8883          /**
8884          * @event keyup
8885          * Fires after the key up
8886          * @param {Roo.form.Field} this
8887          * @param {Roo.EventObject}  e The event Object
8888          */
8889         keyup : true
8890     });
8891 };
8892
8893 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8894      /**
8895      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8896       automatic validation (defaults to "keyup").
8897      */
8898     validationEvent : "keyup",
8899      /**
8900      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8901      */
8902     validateOnBlur : true,
8903     /**
8904      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8905      */
8906     validationDelay : 250,
8907      /**
8908      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8909      */
8910     focusClass : "x-form-focus",  // not needed???
8911     
8912        
8913     /**
8914      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8915      */
8916     invalidClass : "has-warning",
8917     
8918     /**
8919      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8920      */
8921     validClass : "has-success",
8922     
8923     /**
8924      * @cfg {Boolean} hasFeedback (true|false) default true
8925      */
8926     hasFeedback : true,
8927     
8928     /**
8929      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8930      */
8931     invalidFeedbackClass : "glyphicon-warning-sign",
8932     
8933     /**
8934      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8935      */
8936     validFeedbackClass : "glyphicon-ok",
8937     
8938     /**
8939      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8940      */
8941     selectOnFocus : false,
8942     
8943      /**
8944      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8945      */
8946     maskRe : null,
8947        /**
8948      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8949      */
8950     vtype : null,
8951     
8952       /**
8953      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8954      */
8955     disableKeyFilter : false,
8956     
8957        /**
8958      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8959      */
8960     disabled : false,
8961      /**
8962      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8963      */
8964     allowBlank : true,
8965     /**
8966      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8967      */
8968     blankText : "Please complete this mandatory field",
8969     
8970      /**
8971      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8972      */
8973     minLength : 0,
8974     /**
8975      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8976      */
8977     maxLength : Number.MAX_VALUE,
8978     /**
8979      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8980      */
8981     minLengthText : "The minimum length for this field is {0}",
8982     /**
8983      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8984      */
8985     maxLengthText : "The maximum length for this field is {0}",
8986   
8987     
8988     /**
8989      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8990      * If available, this function will be called only after the basic validators all return true, and will be passed the
8991      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8992      */
8993     validator : null,
8994     /**
8995      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8996      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8997      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8998      */
8999     regex : null,
9000     /**
9001      * @cfg {String} regexText -- Depricated - use Invalid Text
9002      */
9003     regexText : "",
9004     
9005     /**
9006      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9007      */
9008     invalidText : "",
9009     
9010     
9011     
9012     autocomplete: false,
9013     
9014     
9015     fieldLabel : '',
9016     inputType : 'text',
9017     
9018     name : false,
9019     placeholder: false,
9020     before : false,
9021     after : false,
9022     size : false,
9023     hasFocus : false,
9024     preventMark: false,
9025     isFormField : true,
9026     value : '',
9027     labelWidth : 2,
9028     labelAlign : false,
9029     readOnly : false,
9030     align : false,
9031     formatedValue : false,
9032     forceFeedback : false,
9033     
9034     indicatorpos : 'left',
9035     
9036     labellg : 0,
9037     labelmd : 0,
9038     labelsm : 0,
9039     labelxs : 0,
9040     
9041     capture : '',
9042     accept : '',
9043     
9044     parentLabelAlign : function()
9045     {
9046         var parent = this;
9047         while (parent.parent()) {
9048             parent = parent.parent();
9049             if (typeof(parent.labelAlign) !='undefined') {
9050                 return parent.labelAlign;
9051             }
9052         }
9053         return 'left';
9054         
9055     },
9056     
9057     getAutoCreate : function()
9058     {
9059         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9060         
9061         var id = Roo.id();
9062         
9063         var cfg = {};
9064         
9065         if(this.inputType != 'hidden'){
9066             cfg.cls = 'form-group' //input-group
9067         }
9068         
9069         var input =  {
9070             tag: 'input',
9071             id : id,
9072             type : this.inputType,
9073             value : this.value,
9074             cls : 'form-control',
9075             placeholder : this.placeholder || '',
9076             autocomplete : this.autocomplete || 'new-password'
9077         };
9078         
9079         if(this.capture.length){
9080             input.capture = this.capture;
9081         }
9082         
9083         if(this.accept.length){
9084             input.accept = this.accept + "/*";
9085         }
9086         
9087         if(this.align){
9088             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9089         }
9090         
9091         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9092             input.maxLength = this.maxLength;
9093         }
9094         
9095         if (this.disabled) {
9096             input.disabled=true;
9097         }
9098         
9099         if (this.readOnly) {
9100             input.readonly=true;
9101         }
9102         
9103         if (this.name) {
9104             input.name = this.name;
9105         }
9106         
9107         if (this.size) {
9108             input.cls += ' input-' + this.size;
9109         }
9110         
9111         var settings=this;
9112         ['xs','sm','md','lg'].map(function(size){
9113             if (settings[size]) {
9114                 cfg.cls += ' col-' + size + '-' + settings[size];
9115             }
9116         });
9117         
9118         var inputblock = input;
9119         
9120         var feedback = {
9121             tag: 'span',
9122             cls: 'glyphicon form-control-feedback'
9123         };
9124             
9125         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9126             
9127             inputblock = {
9128                 cls : 'has-feedback',
9129                 cn :  [
9130                     input,
9131                     feedback
9132                 ] 
9133             };  
9134         }
9135         
9136         if (this.before || this.after) {
9137             
9138             inputblock = {
9139                 cls : 'input-group',
9140                 cn :  [] 
9141             };
9142             
9143             if (this.before && typeof(this.before) == 'string') {
9144                 
9145                 inputblock.cn.push({
9146                     tag :'span',
9147                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9148                     html : this.before
9149                 });
9150             }
9151             if (this.before && typeof(this.before) == 'object') {
9152                 this.before = Roo.factory(this.before);
9153                 
9154                 inputblock.cn.push({
9155                     tag :'span',
9156                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9157                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9158                 });
9159             }
9160             
9161             inputblock.cn.push(input);
9162             
9163             if (this.after && typeof(this.after) == 'string') {
9164                 inputblock.cn.push({
9165                     tag :'span',
9166                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9167                     html : this.after
9168                 });
9169             }
9170             if (this.after && typeof(this.after) == 'object') {
9171                 this.after = Roo.factory(this.after);
9172                 
9173                 inputblock.cn.push({
9174                     tag :'span',
9175                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9176                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9177                 });
9178             }
9179             
9180             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9181                 inputblock.cls += ' has-feedback';
9182                 inputblock.cn.push(feedback);
9183             }
9184         };
9185         var indicator = {
9186             tag : 'i',
9187             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9188             tooltip : 'This field is required'
9189         };
9190         if (Roo.bootstrap.version == 4) {
9191             indicator = {
9192                 tag : 'i',
9193                 style : 'display-none'
9194             };
9195         }
9196         if (align ==='left' && this.fieldLabel.length) {
9197             
9198             cfg.cls += ' roo-form-group-label-left row';
9199             
9200             cfg.cn = [
9201                 indicator,
9202                 {
9203                     tag: 'label',
9204                     'for' :  id,
9205                     cls : 'control-label col-form-label',
9206                     html : this.fieldLabel
9207
9208                 },
9209                 {
9210                     cls : "", 
9211                     cn: [
9212                         inputblock
9213                     ]
9214                 }
9215             ];
9216             
9217             var labelCfg = cfg.cn[1];
9218             var contentCfg = cfg.cn[2];
9219             
9220             if(this.indicatorpos == 'right'){
9221                 cfg.cn = [
9222                     {
9223                         tag: 'label',
9224                         'for' :  id,
9225                         cls : 'control-label col-form-label',
9226                         cn : [
9227                             {
9228                                 tag : 'span',
9229                                 html : this.fieldLabel
9230                             },
9231                             indicator
9232                         ]
9233                     },
9234                     {
9235                         cls : "",
9236                         cn: [
9237                             inputblock
9238                         ]
9239                     }
9240
9241                 ];
9242                 
9243                 labelCfg = cfg.cn[0];
9244                 contentCfg = cfg.cn[1];
9245             
9246             }
9247             
9248             if(this.labelWidth > 12){
9249                 labelCfg.style = "width: " + this.labelWidth + 'px';
9250             }
9251             
9252             if(this.labelWidth < 13 && this.labelmd == 0){
9253                 this.labelmd = this.labelWidth;
9254             }
9255             
9256             if(this.labellg > 0){
9257                 labelCfg.cls += ' col-lg-' + this.labellg;
9258                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9259             }
9260             
9261             if(this.labelmd > 0){
9262                 labelCfg.cls += ' col-md-' + this.labelmd;
9263                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9264             }
9265             
9266             if(this.labelsm > 0){
9267                 labelCfg.cls += ' col-sm-' + this.labelsm;
9268                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9269             }
9270             
9271             if(this.labelxs > 0){
9272                 labelCfg.cls += ' col-xs-' + this.labelxs;
9273                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9274             }
9275             
9276             
9277         } else if ( this.fieldLabel.length) {
9278                 
9279             cfg.cn = [
9280                 {
9281                     tag : 'i',
9282                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9283                     tooltip : 'This field is required'
9284                 },
9285                 {
9286                     tag: 'label',
9287                    //cls : 'input-group-addon',
9288                     html : this.fieldLabel
9289
9290                 },
9291
9292                inputblock
9293
9294            ];
9295            
9296            if(this.indicatorpos == 'right'){
9297                 
9298                 cfg.cn = [
9299                     {
9300                         tag: 'label',
9301                        //cls : 'input-group-addon',
9302                         html : this.fieldLabel
9303
9304                     },
9305                     {
9306                         tag : 'i',
9307                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9308                         tooltip : 'This field is required'
9309                     },
9310
9311                    inputblock
9312
9313                ];
9314
9315             }
9316
9317         } else {
9318             
9319             cfg.cn = [
9320
9321                     inputblock
9322
9323             ];
9324                 
9325                 
9326         };
9327         
9328         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9329            cfg.cls += ' navbar-form';
9330         }
9331         
9332         if (this.parentType === 'NavGroup') {
9333            cfg.cls += ' navbar-form';
9334            cfg.tag = 'li';
9335         }
9336         
9337         return cfg;
9338         
9339     },
9340     /**
9341      * return the real input element.
9342      */
9343     inputEl: function ()
9344     {
9345         return this.el.select('input.form-control',true).first();
9346     },
9347     
9348     tooltipEl : function()
9349     {
9350         return this.inputEl();
9351     },
9352     
9353     indicatorEl : function()
9354     {
9355         if (Roo.bootstrap.version == 4) {
9356             return false; // not enabled in v4 yet.
9357         }
9358         
9359         var indicator = this.el.select('i.roo-required-indicator',true).first();
9360         
9361         if(!indicator){
9362             return false;
9363         }
9364         
9365         return indicator;
9366         
9367     },
9368     
9369     setDisabled : function(v)
9370     {
9371         var i  = this.inputEl().dom;
9372         if (!v) {
9373             i.removeAttribute('disabled');
9374             return;
9375             
9376         }
9377         i.setAttribute('disabled','true');
9378     },
9379     initEvents : function()
9380     {
9381           
9382         this.inputEl().on("keydown" , this.fireKey,  this);
9383         this.inputEl().on("focus", this.onFocus,  this);
9384         this.inputEl().on("blur", this.onBlur,  this);
9385         
9386         this.inputEl().relayEvent('keyup', this);
9387         
9388         this.indicator = this.indicatorEl();
9389         
9390         if(this.indicator){
9391             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9392         }
9393  
9394         // reference to original value for reset
9395         this.originalValue = this.getValue();
9396         //Roo.form.TextField.superclass.initEvents.call(this);
9397         if(this.validationEvent == 'keyup'){
9398             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9399             this.inputEl().on('keyup', this.filterValidation, this);
9400         }
9401         else if(this.validationEvent !== false){
9402             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9403         }
9404         
9405         if(this.selectOnFocus){
9406             this.on("focus", this.preFocus, this);
9407             
9408         }
9409         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9410             this.inputEl().on("keypress", this.filterKeys, this);
9411         } else {
9412             this.inputEl().relayEvent('keypress', this);
9413         }
9414        /* if(this.grow){
9415             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9416             this.el.on("click", this.autoSize,  this);
9417         }
9418         */
9419         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9420             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9421         }
9422         
9423         if (typeof(this.before) == 'object') {
9424             this.before.render(this.el.select('.roo-input-before',true).first());
9425         }
9426         if (typeof(this.after) == 'object') {
9427             this.after.render(this.el.select('.roo-input-after',true).first());
9428         }
9429         
9430         this.inputEl().on('change', this.onChange, this);
9431         
9432     },
9433     filterValidation : function(e){
9434         if(!e.isNavKeyPress()){
9435             this.validationTask.delay(this.validationDelay);
9436         }
9437     },
9438      /**
9439      * Validates the field value
9440      * @return {Boolean} True if the value is valid, else false
9441      */
9442     validate : function(){
9443         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9444         if(this.disabled || this.validateValue(this.getRawValue())){
9445             this.markValid();
9446             return true;
9447         }
9448         
9449         this.markInvalid();
9450         return false;
9451     },
9452     
9453     
9454     /**
9455      * Validates a value according to the field's validation rules and marks the field as invalid
9456      * if the validation fails
9457      * @param {Mixed} value The value to validate
9458      * @return {Boolean} True if the value is valid, else false
9459      */
9460     validateValue : function(value)
9461     {
9462         if(this.getVisibilityEl().hasClass('hidden')){
9463             return true;
9464         }
9465         
9466         if(value.length < 1)  { // if it's blank
9467             if(this.allowBlank){
9468                 return true;
9469             }
9470             return false;
9471         }
9472         
9473         if(value.length < this.minLength){
9474             return false;
9475         }
9476         if(value.length > this.maxLength){
9477             return false;
9478         }
9479         if(this.vtype){
9480             var vt = Roo.form.VTypes;
9481             if(!vt[this.vtype](value, this)){
9482                 return false;
9483             }
9484         }
9485         if(typeof this.validator == "function"){
9486             var msg = this.validator(value);
9487             if(msg !== true){
9488                 return false;
9489             }
9490             if (typeof(msg) == 'string') {
9491                 this.invalidText = msg;
9492             }
9493         }
9494         
9495         if(this.regex && !this.regex.test(value)){
9496             return false;
9497         }
9498         
9499         return true;
9500     },
9501     
9502      // private
9503     fireKey : function(e){
9504         //Roo.log('field ' + e.getKey());
9505         if(e.isNavKeyPress()){
9506             this.fireEvent("specialkey", this, e);
9507         }
9508     },
9509     focus : function (selectText){
9510         if(this.rendered){
9511             this.inputEl().focus();
9512             if(selectText === true){
9513                 this.inputEl().dom.select();
9514             }
9515         }
9516         return this;
9517     } ,
9518     
9519     onFocus : function(){
9520         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9521            // this.el.addClass(this.focusClass);
9522         }
9523         if(!this.hasFocus){
9524             this.hasFocus = true;
9525             this.startValue = this.getValue();
9526             this.fireEvent("focus", this);
9527         }
9528     },
9529     
9530     beforeBlur : Roo.emptyFn,
9531
9532     
9533     // private
9534     onBlur : function(){
9535         this.beforeBlur();
9536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9537             //this.el.removeClass(this.focusClass);
9538         }
9539         this.hasFocus = false;
9540         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9541             this.validate();
9542         }
9543         var v = this.getValue();
9544         if(String(v) !== String(this.startValue)){
9545             this.fireEvent('change', this, v, this.startValue);
9546         }
9547         this.fireEvent("blur", this);
9548     },
9549     
9550     onChange : function(e)
9551     {
9552         var v = this.getValue();
9553         if(String(v) !== String(this.startValue)){
9554             this.fireEvent('change', this, v, this.startValue);
9555         }
9556         
9557     },
9558     
9559     /**
9560      * Resets the current field value to the originally loaded value and clears any validation messages
9561      */
9562     reset : function(){
9563         this.setValue(this.originalValue);
9564         this.validate();
9565     },
9566      /**
9567      * Returns the name of the field
9568      * @return {Mixed} name The name field
9569      */
9570     getName: function(){
9571         return this.name;
9572     },
9573      /**
9574      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9575      * @return {Mixed} value The field value
9576      */
9577     getValue : function(){
9578         
9579         var v = this.inputEl().getValue();
9580         
9581         return v;
9582     },
9583     /**
9584      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9585      * @return {Mixed} value The field value
9586      */
9587     getRawValue : function(){
9588         var v = this.inputEl().getValue();
9589         
9590         return v;
9591     },
9592     
9593     /**
9594      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9595      * @param {Mixed} value The value to set
9596      */
9597     setRawValue : function(v){
9598         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9599     },
9600     
9601     selectText : function(start, end){
9602         var v = this.getRawValue();
9603         if(v.length > 0){
9604             start = start === undefined ? 0 : start;
9605             end = end === undefined ? v.length : end;
9606             var d = this.inputEl().dom;
9607             if(d.setSelectionRange){
9608                 d.setSelectionRange(start, end);
9609             }else if(d.createTextRange){
9610                 var range = d.createTextRange();
9611                 range.moveStart("character", start);
9612                 range.moveEnd("character", v.length-end);
9613                 range.select();
9614             }
9615         }
9616     },
9617     
9618     /**
9619      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9620      * @param {Mixed} value The value to set
9621      */
9622     setValue : function(v){
9623         this.value = v;
9624         if(this.rendered){
9625             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9626             this.validate();
9627         }
9628     },
9629     
9630     /*
9631     processValue : function(value){
9632         if(this.stripCharsRe){
9633             var newValue = value.replace(this.stripCharsRe, '');
9634             if(newValue !== value){
9635                 this.setRawValue(newValue);
9636                 return newValue;
9637             }
9638         }
9639         return value;
9640     },
9641   */
9642     preFocus : function(){
9643         
9644         if(this.selectOnFocus){
9645             this.inputEl().dom.select();
9646         }
9647     },
9648     filterKeys : function(e){
9649         var k = e.getKey();
9650         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9651             return;
9652         }
9653         var c = e.getCharCode(), cc = String.fromCharCode(c);
9654         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9655             return;
9656         }
9657         if(!this.maskRe.test(cc)){
9658             e.stopEvent();
9659         }
9660     },
9661      /**
9662      * Clear any invalid styles/messages for this field
9663      */
9664     clearInvalid : function(){
9665         
9666         if(!this.el || this.preventMark){ // not rendered
9667             return;
9668         }
9669         
9670      
9671         this.el.removeClass(this.invalidClass);
9672         
9673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9674             
9675             var feedback = this.el.select('.form-control-feedback', true).first();
9676             
9677             if(feedback){
9678                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9679             }
9680             
9681         }
9682         
9683         if(this.indicator){
9684             this.indicator.removeClass('visible');
9685             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9686         }
9687         
9688         this.fireEvent('valid', this);
9689     },
9690     
9691      /**
9692      * Mark this field as valid
9693      */
9694     markValid : function()
9695     {
9696         if(!this.el  || this.preventMark){ // not rendered...
9697             return;
9698         }
9699         
9700         this.el.removeClass([this.invalidClass, this.validClass]);
9701         
9702         var feedback = this.el.select('.form-control-feedback', true).first();
9703             
9704         if(feedback){
9705             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9706         }
9707         
9708         if(this.indicator){
9709             this.indicator.removeClass('visible');
9710             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9711         }
9712         
9713         if(this.disabled){
9714             return;
9715         }
9716         
9717         if(this.allowBlank && !this.getRawValue().length){
9718             return;
9719         }
9720         
9721         this.el.addClass(this.validClass);
9722         
9723         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9724             
9725             var feedback = this.el.select('.form-control-feedback', true).first();
9726             
9727             if(feedback){
9728                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9729                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9730             }
9731             
9732         }
9733         
9734         this.fireEvent('valid', this);
9735     },
9736     
9737      /**
9738      * Mark this field as invalid
9739      * @param {String} msg The validation message
9740      */
9741     markInvalid : function(msg)
9742     {
9743         if(!this.el  || this.preventMark){ // not rendered
9744             return;
9745         }
9746         
9747         this.el.removeClass([this.invalidClass, this.validClass]);
9748         
9749         var feedback = this.el.select('.form-control-feedback', true).first();
9750             
9751         if(feedback){
9752             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9753         }
9754
9755         if(this.disabled){
9756             return;
9757         }
9758         
9759         if(this.allowBlank && !this.getRawValue().length){
9760             return;
9761         }
9762         
9763         if(this.indicator){
9764             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9765             this.indicator.addClass('visible');
9766         }
9767         
9768         this.el.addClass(this.invalidClass);
9769         
9770         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9771             
9772             var feedback = this.el.select('.form-control-feedback', true).first();
9773             
9774             if(feedback){
9775                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9776                 
9777                 if(this.getValue().length || this.forceFeedback){
9778                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9779                 }
9780                 
9781             }
9782             
9783         }
9784         
9785         this.fireEvent('invalid', this, msg);
9786     },
9787     // private
9788     SafariOnKeyDown : function(event)
9789     {
9790         // this is a workaround for a password hang bug on chrome/ webkit.
9791         if (this.inputEl().dom.type != 'password') {
9792             return;
9793         }
9794         
9795         var isSelectAll = false;
9796         
9797         if(this.inputEl().dom.selectionEnd > 0){
9798             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9799         }
9800         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9801             event.preventDefault();
9802             this.setValue('');
9803             return;
9804         }
9805         
9806         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9807             
9808             event.preventDefault();
9809             // this is very hacky as keydown always get's upper case.
9810             //
9811             var cc = String.fromCharCode(event.getCharCode());
9812             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9813             
9814         }
9815     },
9816     adjustWidth : function(tag, w){
9817         tag = tag.toLowerCase();
9818         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9819             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9820                 if(tag == 'input'){
9821                     return w + 2;
9822                 }
9823                 if(tag == 'textarea'){
9824                     return w-2;
9825                 }
9826             }else if(Roo.isOpera){
9827                 if(tag == 'input'){
9828                     return w + 2;
9829                 }
9830                 if(tag == 'textarea'){
9831                     return w-2;
9832                 }
9833             }
9834         }
9835         return w;
9836     },
9837     
9838     setFieldLabel : function(v)
9839     {
9840         if(!this.rendered){
9841             return;
9842         }
9843         
9844         if(this.indicatorEl()){
9845             var ar = this.el.select('label > span',true);
9846             
9847             if (ar.elements.length) {
9848                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9849                 this.fieldLabel = v;
9850                 return;
9851             }
9852             
9853             var br = this.el.select('label',true);
9854             
9855             if(br.elements.length) {
9856                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9857                 this.fieldLabel = v;
9858                 return;
9859             }
9860             
9861             Roo.log('Cannot Found any of label > span || label in input');
9862             return;
9863         }
9864         
9865         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9866         this.fieldLabel = v;
9867         
9868         
9869     }
9870 });
9871
9872  
9873 /*
9874  * - LGPL
9875  *
9876  * Input
9877  * 
9878  */
9879
9880 /**
9881  * @class Roo.bootstrap.TextArea
9882  * @extends Roo.bootstrap.Input
9883  * Bootstrap TextArea class
9884  * @cfg {Number} cols Specifies the visible width of a text area
9885  * @cfg {Number} rows Specifies the visible number of lines in a text area
9886  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9887  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9888  * @cfg {string} html text
9889  * 
9890  * @constructor
9891  * Create a new TextArea
9892  * @param {Object} config The config object
9893  */
9894
9895 Roo.bootstrap.TextArea = function(config){
9896     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9897    
9898 };
9899
9900 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9901      
9902     cols : false,
9903     rows : 5,
9904     readOnly : false,
9905     warp : 'soft',
9906     resize : false,
9907     value: false,
9908     html: false,
9909     
9910     getAutoCreate : function(){
9911         
9912         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9913         
9914         var id = Roo.id();
9915         
9916         var cfg = {};
9917         
9918         if(this.inputType != 'hidden'){
9919             cfg.cls = 'form-group' //input-group
9920         }
9921         
9922         var input =  {
9923             tag: 'textarea',
9924             id : id,
9925             warp : this.warp,
9926             rows : this.rows,
9927             value : this.value || '',
9928             html: this.html || '',
9929             cls : 'form-control',
9930             placeholder : this.placeholder || '' 
9931             
9932         };
9933         
9934         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9935             input.maxLength = this.maxLength;
9936         }
9937         
9938         if(this.resize){
9939             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9940         }
9941         
9942         if(this.cols){
9943             input.cols = this.cols;
9944         }
9945         
9946         if (this.readOnly) {
9947             input.readonly = true;
9948         }
9949         
9950         if (this.name) {
9951             input.name = this.name;
9952         }
9953         
9954         if (this.size) {
9955             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9956         }
9957         
9958         var settings=this;
9959         ['xs','sm','md','lg'].map(function(size){
9960             if (settings[size]) {
9961                 cfg.cls += ' col-' + size + '-' + settings[size];
9962             }
9963         });
9964         
9965         var inputblock = input;
9966         
9967         if(this.hasFeedback && !this.allowBlank){
9968             
9969             var feedback = {
9970                 tag: 'span',
9971                 cls: 'glyphicon form-control-feedback'
9972             };
9973
9974             inputblock = {
9975                 cls : 'has-feedback',
9976                 cn :  [
9977                     input,
9978                     feedback
9979                 ] 
9980             };  
9981         }
9982         
9983         
9984         if (this.before || this.after) {
9985             
9986             inputblock = {
9987                 cls : 'input-group',
9988                 cn :  [] 
9989             };
9990             if (this.before) {
9991                 inputblock.cn.push({
9992                     tag :'span',
9993                     cls : 'input-group-addon',
9994                     html : this.before
9995                 });
9996             }
9997             
9998             inputblock.cn.push(input);
9999             
10000             if(this.hasFeedback && !this.allowBlank){
10001                 inputblock.cls += ' has-feedback';
10002                 inputblock.cn.push(feedback);
10003             }
10004             
10005             if (this.after) {
10006                 inputblock.cn.push({
10007                     tag :'span',
10008                     cls : 'input-group-addon',
10009                     html : this.after
10010                 });
10011             }
10012             
10013         }
10014         
10015         if (align ==='left' && this.fieldLabel.length) {
10016             cfg.cn = [
10017                 {
10018                     tag: 'label',
10019                     'for' :  id,
10020                     cls : 'control-label',
10021                     html : this.fieldLabel
10022                 },
10023                 {
10024                     cls : "",
10025                     cn: [
10026                         inputblock
10027                     ]
10028                 }
10029
10030             ];
10031             
10032             if(this.labelWidth > 12){
10033                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10034             }
10035
10036             if(this.labelWidth < 13 && this.labelmd == 0){
10037                 this.labelmd = this.labelWidth;
10038             }
10039
10040             if(this.labellg > 0){
10041                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10042                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10043             }
10044
10045             if(this.labelmd > 0){
10046                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10047                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10048             }
10049
10050             if(this.labelsm > 0){
10051                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10052                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10053             }
10054
10055             if(this.labelxs > 0){
10056                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10057                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10058             }
10059             
10060         } else if ( this.fieldLabel.length) {
10061             cfg.cn = [
10062
10063                {
10064                    tag: 'label',
10065                    //cls : 'input-group-addon',
10066                    html : this.fieldLabel
10067
10068                },
10069
10070                inputblock
10071
10072            ];
10073
10074         } else {
10075
10076             cfg.cn = [
10077
10078                 inputblock
10079
10080             ];
10081                 
10082         }
10083         
10084         if (this.disabled) {
10085             input.disabled=true;
10086         }
10087         
10088         return cfg;
10089         
10090     },
10091     /**
10092      * return the real textarea element.
10093      */
10094     inputEl: function ()
10095     {
10096         return this.el.select('textarea.form-control',true).first();
10097     },
10098     
10099     /**
10100      * Clear any invalid styles/messages for this field
10101      */
10102     clearInvalid : function()
10103     {
10104         
10105         if(!this.el || this.preventMark){ // not rendered
10106             return;
10107         }
10108         
10109         var label = this.el.select('label', true).first();
10110         var icon = this.el.select('i.fa-star', true).first();
10111         
10112         if(label && icon){
10113             icon.remove();
10114         }
10115         
10116         this.el.removeClass(this.invalidClass);
10117         
10118         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10119             
10120             var feedback = this.el.select('.form-control-feedback', true).first();
10121             
10122             if(feedback){
10123                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10124             }
10125             
10126         }
10127         
10128         this.fireEvent('valid', this);
10129     },
10130     
10131      /**
10132      * Mark this field as valid
10133      */
10134     markValid : function()
10135     {
10136         if(!this.el  || this.preventMark){ // not rendered
10137             return;
10138         }
10139         
10140         this.el.removeClass([this.invalidClass, this.validClass]);
10141         
10142         var feedback = this.el.select('.form-control-feedback', true).first();
10143             
10144         if(feedback){
10145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10146         }
10147
10148         if(this.disabled || this.allowBlank){
10149             return;
10150         }
10151         
10152         var label = this.el.select('label', true).first();
10153         var icon = this.el.select('i.fa-star', true).first();
10154         
10155         if(label && icon){
10156             icon.remove();
10157         }
10158         
10159         this.el.addClass(this.validClass);
10160         
10161         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10162             
10163             var feedback = this.el.select('.form-control-feedback', true).first();
10164             
10165             if(feedback){
10166                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10167                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10168             }
10169             
10170         }
10171         
10172         this.fireEvent('valid', this);
10173     },
10174     
10175      /**
10176      * Mark this field as invalid
10177      * @param {String} msg The validation message
10178      */
10179     markInvalid : function(msg)
10180     {
10181         if(!this.el  || this.preventMark){ // not rendered
10182             return;
10183         }
10184         
10185         this.el.removeClass([this.invalidClass, this.validClass]);
10186         
10187         var feedback = this.el.select('.form-control-feedback', true).first();
10188             
10189         if(feedback){
10190             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10191         }
10192
10193         if(this.disabled || this.allowBlank){
10194             return;
10195         }
10196         
10197         var label = this.el.select('label', true).first();
10198         var icon = this.el.select('i.fa-star', true).first();
10199         
10200         if(!this.getValue().length && label && !icon){
10201             this.el.createChild({
10202                 tag : 'i',
10203                 cls : 'text-danger fa fa-lg fa-star',
10204                 tooltip : 'This field is required',
10205                 style : 'margin-right:5px;'
10206             }, label, true);
10207         }
10208
10209         this.el.addClass(this.invalidClass);
10210         
10211         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10212             
10213             var feedback = this.el.select('.form-control-feedback', true).first();
10214             
10215             if(feedback){
10216                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10217                 
10218                 if(this.getValue().length || this.forceFeedback){
10219                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10220                 }
10221                 
10222             }
10223             
10224         }
10225         
10226         this.fireEvent('invalid', this, msg);
10227     }
10228 });
10229
10230  
10231 /*
10232  * - LGPL
10233  *
10234  * trigger field - base class for combo..
10235  * 
10236  */
10237  
10238 /**
10239  * @class Roo.bootstrap.TriggerField
10240  * @extends Roo.bootstrap.Input
10241  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10242  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10243  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10244  * for which you can provide a custom implementation.  For example:
10245  * <pre><code>
10246 var trigger = new Roo.bootstrap.TriggerField();
10247 trigger.onTriggerClick = myTriggerFn;
10248 trigger.applyTo('my-field');
10249 </code></pre>
10250  *
10251  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10252  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10253  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10254  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10255  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10256
10257  * @constructor
10258  * Create a new TriggerField.
10259  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10260  * to the base TextField)
10261  */
10262 Roo.bootstrap.TriggerField = function(config){
10263     this.mimicing = false;
10264     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10265 };
10266
10267 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10268     /**
10269      * @cfg {String} triggerClass A CSS class to apply to the trigger
10270      */
10271      /**
10272      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10273      */
10274     hideTrigger:false,
10275
10276     /**
10277      * @cfg {Boolean} removable (true|false) special filter default false
10278      */
10279     removable : false,
10280     
10281     /** @cfg {Boolean} grow @hide */
10282     /** @cfg {Number} growMin @hide */
10283     /** @cfg {Number} growMax @hide */
10284
10285     /**
10286      * @hide 
10287      * @method
10288      */
10289     autoSize: Roo.emptyFn,
10290     // private
10291     monitorTab : true,
10292     // private
10293     deferHeight : true,
10294
10295     
10296     actionMode : 'wrap',
10297     
10298     caret : false,
10299     
10300     
10301     getAutoCreate : function(){
10302        
10303         var align = this.labelAlign || this.parentLabelAlign();
10304         
10305         var id = Roo.id();
10306         
10307         var cfg = {
10308             cls: 'form-group' //input-group
10309         };
10310         
10311         
10312         var input =  {
10313             tag: 'input',
10314             id : id,
10315             type : this.inputType,
10316             cls : 'form-control',
10317             autocomplete: 'new-password',
10318             placeholder : this.placeholder || '' 
10319             
10320         };
10321         if (this.name) {
10322             input.name = this.name;
10323         }
10324         if (this.size) {
10325             input.cls += ' input-' + this.size;
10326         }
10327         
10328         if (this.disabled) {
10329             input.disabled=true;
10330         }
10331         
10332         var inputblock = input;
10333         
10334         if(this.hasFeedback && !this.allowBlank){
10335             
10336             var feedback = {
10337                 tag: 'span',
10338                 cls: 'glyphicon form-control-feedback'
10339             };
10340             
10341             if(this.removable && !this.editable && !this.tickable){
10342                 inputblock = {
10343                     cls : 'has-feedback',
10344                     cn :  [
10345                         inputblock,
10346                         {
10347                             tag: 'button',
10348                             html : 'x',
10349                             cls : 'roo-combo-removable-btn close'
10350                         },
10351                         feedback
10352                     ] 
10353                 };
10354             } else {
10355                 inputblock = {
10356                     cls : 'has-feedback',
10357                     cn :  [
10358                         inputblock,
10359                         feedback
10360                     ] 
10361                 };
10362             }
10363
10364         } else {
10365             if(this.removable && !this.editable && !this.tickable){
10366                 inputblock = {
10367                     cls : 'roo-removable',
10368                     cn :  [
10369                         inputblock,
10370                         {
10371                             tag: 'button',
10372                             html : 'x',
10373                             cls : 'roo-combo-removable-btn close'
10374                         }
10375                     ] 
10376                 };
10377             }
10378         }
10379         
10380         if (this.before || this.after) {
10381             
10382             inputblock = {
10383                 cls : 'input-group',
10384                 cn :  [] 
10385             };
10386             if (this.before) {
10387                 inputblock.cn.push({
10388                     tag :'span',
10389                     cls : 'input-group-addon input-group-prepend input-group-text',
10390                     html : this.before
10391                 });
10392             }
10393             
10394             inputblock.cn.push(input);
10395             
10396             if(this.hasFeedback && !this.allowBlank){
10397                 inputblock.cls += ' has-feedback';
10398                 inputblock.cn.push(feedback);
10399             }
10400             
10401             if (this.after) {
10402                 inputblock.cn.push({
10403                     tag :'span',
10404                     cls : 'input-group-addon input-group-append input-group-text',
10405                     html : this.after
10406                 });
10407             }
10408             
10409         };
10410         
10411       
10412         
10413         var ibwrap = inputblock;
10414         
10415         if(this.multiple){
10416             ibwrap = {
10417                 tag: 'ul',
10418                 cls: 'roo-select2-choices',
10419                 cn:[
10420                     {
10421                         tag: 'li',
10422                         cls: 'roo-select2-search-field',
10423                         cn: [
10424
10425                             inputblock
10426                         ]
10427                     }
10428                 ]
10429             };
10430                 
10431         }
10432         
10433         var combobox = {
10434             cls: 'roo-select2-container input-group',
10435             cn: [
10436                  {
10437                     tag: 'input',
10438                     type : 'hidden',
10439                     cls: 'form-hidden-field'
10440                 },
10441                 ibwrap
10442             ]
10443         };
10444         
10445         if(!this.multiple && this.showToggleBtn){
10446             
10447             var caret = {
10448                         tag: 'span',
10449                         cls: 'caret'
10450              };
10451             if (this.caret != false) {
10452                 caret = {
10453                      tag: 'i',
10454                      cls: 'fa fa-' + this.caret
10455                 };
10456                 
10457             }
10458             
10459             combobox.cn.push({
10460                 tag :'span',
10461                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10462                 cn : [
10463                     caret,
10464                     {
10465                         tag: 'span',
10466                         cls: 'combobox-clear',
10467                         cn  : [
10468                             {
10469                                 tag : 'i',
10470                                 cls: 'icon-remove'
10471                             }
10472                         ]
10473                     }
10474                 ]
10475
10476             })
10477         }
10478         
10479         if(this.multiple){
10480             combobox.cls += ' roo-select2-container-multi';
10481         }
10482          var indicator = {
10483             tag : 'i',
10484             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10485             tooltip : 'This field is required'
10486         };
10487         if (Roo.bootstrap.version == 4) {
10488             indicator = {
10489                 tag : 'i',
10490                 style : 'display:none'
10491             };
10492         }
10493         
10494         
10495         if (align ==='left' && this.fieldLabel.length) {
10496             
10497             cfg.cls += ' roo-form-group-label-left row';
10498
10499             cfg.cn = [
10500                 indicator,
10501                 {
10502                     tag: 'label',
10503                     'for' :  id,
10504                     cls : 'control-label',
10505                     html : this.fieldLabel
10506
10507                 },
10508                 {
10509                     cls : "", 
10510                     cn: [
10511                         combobox
10512                     ]
10513                 }
10514
10515             ];
10516             
10517             var labelCfg = cfg.cn[1];
10518             var contentCfg = cfg.cn[2];
10519             
10520             if(this.indicatorpos == 'right'){
10521                 cfg.cn = [
10522                     {
10523                         tag: 'label',
10524                         'for' :  id,
10525                         cls : 'control-label',
10526                         cn : [
10527                             {
10528                                 tag : 'span',
10529                                 html : this.fieldLabel
10530                             },
10531                             indicator
10532                         ]
10533                     },
10534                     {
10535                         cls : "", 
10536                         cn: [
10537                             combobox
10538                         ]
10539                     }
10540
10541                 ];
10542                 
10543                 labelCfg = cfg.cn[0];
10544                 contentCfg = cfg.cn[1];
10545             }
10546             
10547             if(this.labelWidth > 12){
10548                 labelCfg.style = "width: " + this.labelWidth + 'px';
10549             }
10550             
10551             if(this.labelWidth < 13 && this.labelmd == 0){
10552                 this.labelmd = this.labelWidth;
10553             }
10554             
10555             if(this.labellg > 0){
10556                 labelCfg.cls += ' col-lg-' + this.labellg;
10557                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10558             }
10559             
10560             if(this.labelmd > 0){
10561                 labelCfg.cls += ' col-md-' + this.labelmd;
10562                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10563             }
10564             
10565             if(this.labelsm > 0){
10566                 labelCfg.cls += ' col-sm-' + this.labelsm;
10567                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10568             }
10569             
10570             if(this.labelxs > 0){
10571                 labelCfg.cls += ' col-xs-' + this.labelxs;
10572                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10573             }
10574             
10575         } else if ( this.fieldLabel.length) {
10576 //                Roo.log(" label");
10577             cfg.cn = [
10578                 indicator,
10579                {
10580                    tag: 'label',
10581                    //cls : 'input-group-addon',
10582                    html : this.fieldLabel
10583
10584                },
10585
10586                combobox
10587
10588             ];
10589             
10590             if(this.indicatorpos == 'right'){
10591                 
10592                 cfg.cn = [
10593                     {
10594                        tag: 'label',
10595                        cn : [
10596                            {
10597                                tag : 'span',
10598                                html : this.fieldLabel
10599                            },
10600                            indicator
10601                        ]
10602
10603                     },
10604                     combobox
10605
10606                 ];
10607
10608             }
10609
10610         } else {
10611             
10612 //                Roo.log(" no label && no align");
10613                 cfg = combobox
10614                      
10615                 
10616         }
10617         
10618         var settings=this;
10619         ['xs','sm','md','lg'].map(function(size){
10620             if (settings[size]) {
10621                 cfg.cls += ' col-' + size + '-' + settings[size];
10622             }
10623         });
10624         
10625         return cfg;
10626         
10627     },
10628     
10629     
10630     
10631     // private
10632     onResize : function(w, h){
10633 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10634 //        if(typeof w == 'number'){
10635 //            var x = w - this.trigger.getWidth();
10636 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10637 //            this.trigger.setStyle('left', x+'px');
10638 //        }
10639     },
10640
10641     // private
10642     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10643
10644     // private
10645     getResizeEl : function(){
10646         return this.inputEl();
10647     },
10648
10649     // private
10650     getPositionEl : function(){
10651         return this.inputEl();
10652     },
10653
10654     // private
10655     alignErrorIcon : function(){
10656         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10657     },
10658
10659     // private
10660     initEvents : function(){
10661         
10662         this.createList();
10663         
10664         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10665         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10666         if(!this.multiple && this.showToggleBtn){
10667             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10668             if(this.hideTrigger){
10669                 this.trigger.setDisplayed(false);
10670             }
10671             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10672         }
10673         
10674         if(this.multiple){
10675             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10676         }
10677         
10678         if(this.removable && !this.editable && !this.tickable){
10679             var close = this.closeTriggerEl();
10680             
10681             if(close){
10682                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10683                 close.on('click', this.removeBtnClick, this, close);
10684             }
10685         }
10686         
10687         //this.trigger.addClassOnOver('x-form-trigger-over');
10688         //this.trigger.addClassOnClick('x-form-trigger-click');
10689         
10690         //if(!this.width){
10691         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10692         //}
10693     },
10694     
10695     closeTriggerEl : function()
10696     {
10697         var close = this.el.select('.roo-combo-removable-btn', true).first();
10698         return close ? close : false;
10699     },
10700     
10701     removeBtnClick : function(e, h, el)
10702     {
10703         e.preventDefault();
10704         
10705         if(this.fireEvent("remove", this) !== false){
10706             this.reset();
10707             this.fireEvent("afterremove", this)
10708         }
10709     },
10710     
10711     createList : function()
10712     {
10713         this.list = Roo.get(document.body).createChild({
10714             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10715             cls: 'typeahead typeahead-long dropdown-menu',
10716             style: 'display:none'
10717         });
10718         
10719         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10720         
10721     },
10722
10723     // private
10724     initTrigger : function(){
10725        
10726     },
10727
10728     // private
10729     onDestroy : function(){
10730         if(this.trigger){
10731             this.trigger.removeAllListeners();
10732           //  this.trigger.remove();
10733         }
10734         //if(this.wrap){
10735         //    this.wrap.remove();
10736         //}
10737         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10738     },
10739
10740     // private
10741     onFocus : function(){
10742         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10743         /*
10744         if(!this.mimicing){
10745             this.wrap.addClass('x-trigger-wrap-focus');
10746             this.mimicing = true;
10747             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10748             if(this.monitorTab){
10749                 this.el.on("keydown", this.checkTab, this);
10750             }
10751         }
10752         */
10753     },
10754
10755     // private
10756     checkTab : function(e){
10757         if(e.getKey() == e.TAB){
10758             this.triggerBlur();
10759         }
10760     },
10761
10762     // private
10763     onBlur : function(){
10764         // do nothing
10765     },
10766
10767     // private
10768     mimicBlur : function(e, t){
10769         /*
10770         if(!this.wrap.contains(t) && this.validateBlur()){
10771             this.triggerBlur();
10772         }
10773         */
10774     },
10775
10776     // private
10777     triggerBlur : function(){
10778         this.mimicing = false;
10779         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10780         if(this.monitorTab){
10781             this.el.un("keydown", this.checkTab, this);
10782         }
10783         //this.wrap.removeClass('x-trigger-wrap-focus');
10784         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10785     },
10786
10787     // private
10788     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10789     validateBlur : function(e, t){
10790         return true;
10791     },
10792
10793     // private
10794     onDisable : function(){
10795         this.inputEl().dom.disabled = true;
10796         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10797         //if(this.wrap){
10798         //    this.wrap.addClass('x-item-disabled');
10799         //}
10800     },
10801
10802     // private
10803     onEnable : function(){
10804         this.inputEl().dom.disabled = false;
10805         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10806         //if(this.wrap){
10807         //    this.el.removeClass('x-item-disabled');
10808         //}
10809     },
10810
10811     // private
10812     onShow : function(){
10813         var ae = this.getActionEl();
10814         
10815         if(ae){
10816             ae.dom.style.display = '';
10817             ae.dom.style.visibility = 'visible';
10818         }
10819     },
10820
10821     // private
10822     
10823     onHide : function(){
10824         var ae = this.getActionEl();
10825         ae.dom.style.display = 'none';
10826     },
10827
10828     /**
10829      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10830      * by an implementing function.
10831      * @method
10832      * @param {EventObject} e
10833      */
10834     onTriggerClick : Roo.emptyFn
10835 });
10836  /*
10837  * Based on:
10838  * Ext JS Library 1.1.1
10839  * Copyright(c) 2006-2007, Ext JS, LLC.
10840  *
10841  * Originally Released Under LGPL - original licence link has changed is not relivant.
10842  *
10843  * Fork - LGPL
10844  * <script type="text/javascript">
10845  */
10846
10847
10848 /**
10849  * @class Roo.data.SortTypes
10850  * @singleton
10851  * Defines the default sorting (casting?) comparison functions used when sorting data.
10852  */
10853 Roo.data.SortTypes = {
10854     /**
10855      * Default sort that does nothing
10856      * @param {Mixed} s The value being converted
10857      * @return {Mixed} The comparison value
10858      */
10859     none : function(s){
10860         return s;
10861     },
10862     
10863     /**
10864      * The regular expression used to strip tags
10865      * @type {RegExp}
10866      * @property
10867      */
10868     stripTagsRE : /<\/?[^>]+>/gi,
10869     
10870     /**
10871      * Strips all HTML tags to sort on text only
10872      * @param {Mixed} s The value being converted
10873      * @return {String} The comparison value
10874      */
10875     asText : function(s){
10876         return String(s).replace(this.stripTagsRE, "");
10877     },
10878     
10879     /**
10880      * Strips all HTML tags to sort on text only - Case insensitive
10881      * @param {Mixed} s The value being converted
10882      * @return {String} The comparison value
10883      */
10884     asUCText : function(s){
10885         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10886     },
10887     
10888     /**
10889      * Case insensitive string
10890      * @param {Mixed} s The value being converted
10891      * @return {String} The comparison value
10892      */
10893     asUCString : function(s) {
10894         return String(s).toUpperCase();
10895     },
10896     
10897     /**
10898      * Date sorting
10899      * @param {Mixed} s The value being converted
10900      * @return {Number} The comparison value
10901      */
10902     asDate : function(s) {
10903         if(!s){
10904             return 0;
10905         }
10906         if(s instanceof Date){
10907             return s.getTime();
10908         }
10909         return Date.parse(String(s));
10910     },
10911     
10912     /**
10913      * Float sorting
10914      * @param {Mixed} s The value being converted
10915      * @return {Float} The comparison value
10916      */
10917     asFloat : function(s) {
10918         var val = parseFloat(String(s).replace(/,/g, ""));
10919         if(isNaN(val)) {
10920             val = 0;
10921         }
10922         return val;
10923     },
10924     
10925     /**
10926      * Integer sorting
10927      * @param {Mixed} s The value being converted
10928      * @return {Number} The comparison value
10929      */
10930     asInt : function(s) {
10931         var val = parseInt(String(s).replace(/,/g, ""));
10932         if(isNaN(val)) {
10933             val = 0;
10934         }
10935         return val;
10936     }
10937 };/*
10938  * Based on:
10939  * Ext JS Library 1.1.1
10940  * Copyright(c) 2006-2007, Ext JS, LLC.
10941  *
10942  * Originally Released Under LGPL - original licence link has changed is not relivant.
10943  *
10944  * Fork - LGPL
10945  * <script type="text/javascript">
10946  */
10947
10948 /**
10949 * @class Roo.data.Record
10950  * Instances of this class encapsulate both record <em>definition</em> information, and record
10951  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10952  * to access Records cached in an {@link Roo.data.Store} object.<br>
10953  * <p>
10954  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10955  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10956  * objects.<br>
10957  * <p>
10958  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10959  * @constructor
10960  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10961  * {@link #create}. The parameters are the same.
10962  * @param {Array} data An associative Array of data values keyed by the field name.
10963  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10964  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10965  * not specified an integer id is generated.
10966  */
10967 Roo.data.Record = function(data, id){
10968     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10969     this.data = data;
10970 };
10971
10972 /**
10973  * Generate a constructor for a specific record layout.
10974  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10975  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10976  * Each field definition object may contain the following properties: <ul>
10977  * <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,
10978  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10979  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10980  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10981  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10982  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10983  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10984  * this may be omitted.</p></li>
10985  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10986  * <ul><li>auto (Default, implies no conversion)</li>
10987  * <li>string</li>
10988  * <li>int</li>
10989  * <li>float</li>
10990  * <li>boolean</li>
10991  * <li>date</li></ul></p></li>
10992  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10993  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10994  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10995  * by the Reader into an object that will be stored in the Record. It is passed the
10996  * following parameters:<ul>
10997  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10998  * </ul></p></li>
10999  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11000  * </ul>
11001  * <br>usage:<br><pre><code>
11002 var TopicRecord = Roo.data.Record.create(
11003     {name: 'title', mapping: 'topic_title'},
11004     {name: 'author', mapping: 'username'},
11005     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11006     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11007     {name: 'lastPoster', mapping: 'user2'},
11008     {name: 'excerpt', mapping: 'post_text'}
11009 );
11010
11011 var myNewRecord = new TopicRecord({
11012     title: 'Do my job please',
11013     author: 'noobie',
11014     totalPosts: 1,
11015     lastPost: new Date(),
11016     lastPoster: 'Animal',
11017     excerpt: 'No way dude!'
11018 });
11019 myStore.add(myNewRecord);
11020 </code></pre>
11021  * @method create
11022  * @static
11023  */
11024 Roo.data.Record.create = function(o){
11025     var f = function(){
11026         f.superclass.constructor.apply(this, arguments);
11027     };
11028     Roo.extend(f, Roo.data.Record);
11029     var p = f.prototype;
11030     p.fields = new Roo.util.MixedCollection(false, function(field){
11031         return field.name;
11032     });
11033     for(var i = 0, len = o.length; i < len; i++){
11034         p.fields.add(new Roo.data.Field(o[i]));
11035     }
11036     f.getField = function(name){
11037         return p.fields.get(name);  
11038     };
11039     return f;
11040 };
11041
11042 Roo.data.Record.AUTO_ID = 1000;
11043 Roo.data.Record.EDIT = 'edit';
11044 Roo.data.Record.REJECT = 'reject';
11045 Roo.data.Record.COMMIT = 'commit';
11046
11047 Roo.data.Record.prototype = {
11048     /**
11049      * Readonly flag - true if this record has been modified.
11050      * @type Boolean
11051      */
11052     dirty : false,
11053     editing : false,
11054     error: null,
11055     modified: null,
11056
11057     // private
11058     join : function(store){
11059         this.store = store;
11060     },
11061
11062     /**
11063      * Set the named field to the specified value.
11064      * @param {String} name The name of the field to set.
11065      * @param {Object} value The value to set the field to.
11066      */
11067     set : function(name, value){
11068         if(this.data[name] == value){
11069             return;
11070         }
11071         this.dirty = true;
11072         if(!this.modified){
11073             this.modified = {};
11074         }
11075         if(typeof this.modified[name] == 'undefined'){
11076             this.modified[name] = this.data[name];
11077         }
11078         this.data[name] = value;
11079         if(!this.editing && this.store){
11080             this.store.afterEdit(this);
11081         }       
11082     },
11083
11084     /**
11085      * Get the value of the named field.
11086      * @param {String} name The name of the field to get the value of.
11087      * @return {Object} The value of the field.
11088      */
11089     get : function(name){
11090         return this.data[name]; 
11091     },
11092
11093     // private
11094     beginEdit : function(){
11095         this.editing = true;
11096         this.modified = {}; 
11097     },
11098
11099     // private
11100     cancelEdit : function(){
11101         this.editing = false;
11102         delete this.modified;
11103     },
11104
11105     // private
11106     endEdit : function(){
11107         this.editing = false;
11108         if(this.dirty && this.store){
11109             this.store.afterEdit(this);
11110         }
11111     },
11112
11113     /**
11114      * Usually called by the {@link Roo.data.Store} which owns the Record.
11115      * Rejects all changes made to the Record since either creation, or the last commit operation.
11116      * Modified fields are reverted to their original values.
11117      * <p>
11118      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11119      * of reject operations.
11120      */
11121     reject : function(){
11122         var m = this.modified;
11123         for(var n in m){
11124             if(typeof m[n] != "function"){
11125                 this.data[n] = m[n];
11126             }
11127         }
11128         this.dirty = false;
11129         delete this.modified;
11130         this.editing = false;
11131         if(this.store){
11132             this.store.afterReject(this);
11133         }
11134     },
11135
11136     /**
11137      * Usually called by the {@link Roo.data.Store} which owns the Record.
11138      * Commits all changes made to the Record since either creation, or the last commit operation.
11139      * <p>
11140      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11141      * of commit operations.
11142      */
11143     commit : function(){
11144         this.dirty = false;
11145         delete this.modified;
11146         this.editing = false;
11147         if(this.store){
11148             this.store.afterCommit(this);
11149         }
11150     },
11151
11152     // private
11153     hasError : function(){
11154         return this.error != null;
11155     },
11156
11157     // private
11158     clearError : function(){
11159         this.error = null;
11160     },
11161
11162     /**
11163      * Creates a copy of this record.
11164      * @param {String} id (optional) A new record id if you don't want to use this record's id
11165      * @return {Record}
11166      */
11167     copy : function(newId) {
11168         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11169     }
11170 };/*
11171  * Based on:
11172  * Ext JS Library 1.1.1
11173  * Copyright(c) 2006-2007, Ext JS, LLC.
11174  *
11175  * Originally Released Under LGPL - original licence link has changed is not relivant.
11176  *
11177  * Fork - LGPL
11178  * <script type="text/javascript">
11179  */
11180
11181
11182
11183 /**
11184  * @class Roo.data.Store
11185  * @extends Roo.util.Observable
11186  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11187  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11188  * <p>
11189  * 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
11190  * has no knowledge of the format of the data returned by the Proxy.<br>
11191  * <p>
11192  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11193  * instances from the data object. These records are cached and made available through accessor functions.
11194  * @constructor
11195  * Creates a new Store.
11196  * @param {Object} config A config object containing the objects needed for the Store to access data,
11197  * and read the data into Records.
11198  */
11199 Roo.data.Store = function(config){
11200     this.data = new Roo.util.MixedCollection(false);
11201     this.data.getKey = function(o){
11202         return o.id;
11203     };
11204     this.baseParams = {};
11205     // private
11206     this.paramNames = {
11207         "start" : "start",
11208         "limit" : "limit",
11209         "sort" : "sort",
11210         "dir" : "dir",
11211         "multisort" : "_multisort"
11212     };
11213
11214     if(config && config.data){
11215         this.inlineData = config.data;
11216         delete config.data;
11217     }
11218
11219     Roo.apply(this, config);
11220     
11221     if(this.reader){ // reader passed
11222         this.reader = Roo.factory(this.reader, Roo.data);
11223         this.reader.xmodule = this.xmodule || false;
11224         if(!this.recordType){
11225             this.recordType = this.reader.recordType;
11226         }
11227         if(this.reader.onMetaChange){
11228             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11229         }
11230     }
11231
11232     if(this.recordType){
11233         this.fields = this.recordType.prototype.fields;
11234     }
11235     this.modified = [];
11236
11237     this.addEvents({
11238         /**
11239          * @event datachanged
11240          * Fires when the data cache has changed, and a widget which is using this Store
11241          * as a Record cache should refresh its view.
11242          * @param {Store} this
11243          */
11244         datachanged : true,
11245         /**
11246          * @event metachange
11247          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11248          * @param {Store} this
11249          * @param {Object} meta The JSON metadata
11250          */
11251         metachange : true,
11252         /**
11253          * @event add
11254          * Fires when Records have been added to the Store
11255          * @param {Store} this
11256          * @param {Roo.data.Record[]} records The array of Records added
11257          * @param {Number} index The index at which the record(s) were added
11258          */
11259         add : true,
11260         /**
11261          * @event remove
11262          * Fires when a Record has been removed from the Store
11263          * @param {Store} this
11264          * @param {Roo.data.Record} record The Record that was removed
11265          * @param {Number} index The index at which the record was removed
11266          */
11267         remove : true,
11268         /**
11269          * @event update
11270          * Fires when a Record has been updated
11271          * @param {Store} this
11272          * @param {Roo.data.Record} record The Record that was updated
11273          * @param {String} operation The update operation being performed.  Value may be one of:
11274          * <pre><code>
11275  Roo.data.Record.EDIT
11276  Roo.data.Record.REJECT
11277  Roo.data.Record.COMMIT
11278          * </code></pre>
11279          */
11280         update : true,
11281         /**
11282          * @event clear
11283          * Fires when the data cache has been cleared.
11284          * @param {Store} this
11285          */
11286         clear : true,
11287         /**
11288          * @event beforeload
11289          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11290          * the load action will be canceled.
11291          * @param {Store} this
11292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11293          */
11294         beforeload : true,
11295         /**
11296          * @event beforeloadadd
11297          * Fires after a new set of Records has been loaded.
11298          * @param {Store} this
11299          * @param {Roo.data.Record[]} records The Records that were loaded
11300          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11301          */
11302         beforeloadadd : true,
11303         /**
11304          * @event load
11305          * Fires after a new set of Records has been loaded, before they are added to the store.
11306          * @param {Store} this
11307          * @param {Roo.data.Record[]} records The Records that were loaded
11308          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11309          * @params {Object} return from reader
11310          */
11311         load : true,
11312         /**
11313          * @event loadexception
11314          * Fires if an exception occurs in the Proxy during loading.
11315          * Called with the signature of the Proxy's "loadexception" event.
11316          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11317          * 
11318          * @param {Proxy} 
11319          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11320          * @param {Object} load options 
11321          * @param {Object} jsonData from your request (normally this contains the Exception)
11322          */
11323         loadexception : true
11324     });
11325     
11326     if(this.proxy){
11327         this.proxy = Roo.factory(this.proxy, Roo.data);
11328         this.proxy.xmodule = this.xmodule || false;
11329         this.relayEvents(this.proxy,  ["loadexception"]);
11330     }
11331     this.sortToggle = {};
11332     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11333
11334     Roo.data.Store.superclass.constructor.call(this);
11335
11336     if(this.inlineData){
11337         this.loadData(this.inlineData);
11338         delete this.inlineData;
11339     }
11340 };
11341
11342 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11343      /**
11344     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11345     * without a remote query - used by combo/forms at present.
11346     */
11347     
11348     /**
11349     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11350     */
11351     /**
11352     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11353     */
11354     /**
11355     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11356     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11357     */
11358     /**
11359     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11360     * on any HTTP request
11361     */
11362     /**
11363     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11364     */
11365     /**
11366     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11367     */
11368     multiSort: false,
11369     /**
11370     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11371     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11372     */
11373     remoteSort : false,
11374
11375     /**
11376     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11377      * loaded or when a record is removed. (defaults to false).
11378     */
11379     pruneModifiedRecords : false,
11380
11381     // private
11382     lastOptions : null,
11383
11384     /**
11385      * Add Records to the Store and fires the add event.
11386      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11387      */
11388     add : function(records){
11389         records = [].concat(records);
11390         for(var i = 0, len = records.length; i < len; i++){
11391             records[i].join(this);
11392         }
11393         var index = this.data.length;
11394         this.data.addAll(records);
11395         this.fireEvent("add", this, records, index);
11396     },
11397
11398     /**
11399      * Remove a Record from the Store and fires the remove event.
11400      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11401      */
11402     remove : function(record){
11403         var index = this.data.indexOf(record);
11404         this.data.removeAt(index);
11405  
11406         if(this.pruneModifiedRecords){
11407             this.modified.remove(record);
11408         }
11409         this.fireEvent("remove", this, record, index);
11410     },
11411
11412     /**
11413      * Remove all Records from the Store and fires the clear event.
11414      */
11415     removeAll : function(){
11416         this.data.clear();
11417         if(this.pruneModifiedRecords){
11418             this.modified = [];
11419         }
11420         this.fireEvent("clear", this);
11421     },
11422
11423     /**
11424      * Inserts Records to the Store at the given index and fires the add event.
11425      * @param {Number} index The start index at which to insert the passed Records.
11426      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11427      */
11428     insert : function(index, records){
11429         records = [].concat(records);
11430         for(var i = 0, len = records.length; i < len; i++){
11431             this.data.insert(index, records[i]);
11432             records[i].join(this);
11433         }
11434         this.fireEvent("add", this, records, index);
11435     },
11436
11437     /**
11438      * Get the index within the cache of the passed Record.
11439      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11440      * @return {Number} The index of the passed Record. Returns -1 if not found.
11441      */
11442     indexOf : function(record){
11443         return this.data.indexOf(record);
11444     },
11445
11446     /**
11447      * Get the index within the cache of the Record with the passed id.
11448      * @param {String} id The id of the Record to find.
11449      * @return {Number} The index of the Record. Returns -1 if not found.
11450      */
11451     indexOfId : function(id){
11452         return this.data.indexOfKey(id);
11453     },
11454
11455     /**
11456      * Get the Record with the specified id.
11457      * @param {String} id The id of the Record to find.
11458      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11459      */
11460     getById : function(id){
11461         return this.data.key(id);
11462     },
11463
11464     /**
11465      * Get the Record at the specified index.
11466      * @param {Number} index The index of the Record to find.
11467      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11468      */
11469     getAt : function(index){
11470         return this.data.itemAt(index);
11471     },
11472
11473     /**
11474      * Returns a range of Records between specified indices.
11475      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11476      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11477      * @return {Roo.data.Record[]} An array of Records
11478      */
11479     getRange : function(start, end){
11480         return this.data.getRange(start, end);
11481     },
11482
11483     // private
11484     storeOptions : function(o){
11485         o = Roo.apply({}, o);
11486         delete o.callback;
11487         delete o.scope;
11488         this.lastOptions = o;
11489     },
11490
11491     /**
11492      * Loads the Record cache from the configured Proxy using the configured Reader.
11493      * <p>
11494      * If using remote paging, then the first load call must specify the <em>start</em>
11495      * and <em>limit</em> properties in the options.params property to establish the initial
11496      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11497      * <p>
11498      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11499      * and this call will return before the new data has been loaded. Perform any post-processing
11500      * in a callback function, or in a "load" event handler.</strong>
11501      * <p>
11502      * @param {Object} options An object containing properties which control loading options:<ul>
11503      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11504      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11505      * passed the following arguments:<ul>
11506      * <li>r : Roo.data.Record[]</li>
11507      * <li>options: Options object from the load call</li>
11508      * <li>success: Boolean success indicator</li></ul></li>
11509      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11510      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11511      * </ul>
11512      */
11513     load : function(options){
11514         options = options || {};
11515         if(this.fireEvent("beforeload", this, options) !== false){
11516             this.storeOptions(options);
11517             var p = Roo.apply(options.params || {}, this.baseParams);
11518             // if meta was not loaded from remote source.. try requesting it.
11519             if (!this.reader.metaFromRemote) {
11520                 p._requestMeta = 1;
11521             }
11522             if(this.sortInfo && this.remoteSort){
11523                 var pn = this.paramNames;
11524                 p[pn["sort"]] = this.sortInfo.field;
11525                 p[pn["dir"]] = this.sortInfo.direction;
11526             }
11527             if (this.multiSort) {
11528                 var pn = this.paramNames;
11529                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11530             }
11531             
11532             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11533         }
11534     },
11535
11536     /**
11537      * Reloads the Record cache from the configured Proxy using the configured Reader and
11538      * the options from the last load operation performed.
11539      * @param {Object} options (optional) An object containing properties which may override the options
11540      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11541      * the most recently used options are reused).
11542      */
11543     reload : function(options){
11544         this.load(Roo.applyIf(options||{}, this.lastOptions));
11545     },
11546
11547     // private
11548     // Called as a callback by the Reader during a load operation.
11549     loadRecords : function(o, options, success){
11550         if(!o || success === false){
11551             if(success !== false){
11552                 this.fireEvent("load", this, [], options, o);
11553             }
11554             if(options.callback){
11555                 options.callback.call(options.scope || this, [], options, false);
11556             }
11557             return;
11558         }
11559         // if data returned failure - throw an exception.
11560         if (o.success === false) {
11561             // show a message if no listener is registered.
11562             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11563                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11564             }
11565             // loadmask wil be hooked into this..
11566             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11567             return;
11568         }
11569         var r = o.records, t = o.totalRecords || r.length;
11570         
11571         this.fireEvent("beforeloadadd", this, r, options, o);
11572         
11573         if(!options || options.add !== true){
11574             if(this.pruneModifiedRecords){
11575                 this.modified = [];
11576             }
11577             for(var i = 0, len = r.length; i < len; i++){
11578                 r[i].join(this);
11579             }
11580             if(this.snapshot){
11581                 this.data = this.snapshot;
11582                 delete this.snapshot;
11583             }
11584             this.data.clear();
11585             this.data.addAll(r);
11586             this.totalLength = t;
11587             this.applySort();
11588             this.fireEvent("datachanged", this);
11589         }else{
11590             this.totalLength = Math.max(t, this.data.length+r.length);
11591             this.add(r);
11592         }
11593         
11594         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11595                 
11596             var e = new Roo.data.Record({});
11597
11598             e.set(this.parent.displayField, this.parent.emptyTitle);
11599             e.set(this.parent.valueField, '');
11600
11601             this.insert(0, e);
11602         }
11603             
11604         this.fireEvent("load", this, r, options, o);
11605         if(options.callback){
11606             options.callback.call(options.scope || this, r, options, true);
11607         }
11608     },
11609
11610
11611     /**
11612      * Loads data from a passed data block. A Reader which understands the format of the data
11613      * must have been configured in the constructor.
11614      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11615      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11616      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11617      */
11618     loadData : function(o, append){
11619         var r = this.reader.readRecords(o);
11620         this.loadRecords(r, {add: append}, true);
11621     },
11622
11623     /**
11624      * Gets the number of cached records.
11625      * <p>
11626      * <em>If using paging, this may not be the total size of the dataset. If the data object
11627      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11628      * the data set size</em>
11629      */
11630     getCount : function(){
11631         return this.data.length || 0;
11632     },
11633
11634     /**
11635      * Gets the total number of records in the dataset as returned by the server.
11636      * <p>
11637      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11638      * the dataset size</em>
11639      */
11640     getTotalCount : function(){
11641         return this.totalLength || 0;
11642     },
11643
11644     /**
11645      * Returns the sort state of the Store as an object with two properties:
11646      * <pre><code>
11647  field {String} The name of the field by which the Records are sorted
11648  direction {String} The sort order, "ASC" or "DESC"
11649      * </code></pre>
11650      */
11651     getSortState : function(){
11652         return this.sortInfo;
11653     },
11654
11655     // private
11656     applySort : function(){
11657         if(this.sortInfo && !this.remoteSort){
11658             var s = this.sortInfo, f = s.field;
11659             var st = this.fields.get(f).sortType;
11660             var fn = function(r1, r2){
11661                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11662                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11663             };
11664             this.data.sort(s.direction, fn);
11665             if(this.snapshot && this.snapshot != this.data){
11666                 this.snapshot.sort(s.direction, fn);
11667             }
11668         }
11669     },
11670
11671     /**
11672      * Sets the default sort column and order to be used by the next load operation.
11673      * @param {String} fieldName The name of the field to sort by.
11674      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11675      */
11676     setDefaultSort : function(field, dir){
11677         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11678     },
11679
11680     /**
11681      * Sort the Records.
11682      * If remote sorting is used, the sort is performed on the server, and the cache is
11683      * reloaded. If local sorting is used, the cache is sorted internally.
11684      * @param {String} fieldName The name of the field to sort by.
11685      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11686      */
11687     sort : function(fieldName, dir){
11688         var f = this.fields.get(fieldName);
11689         if(!dir){
11690             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11691             
11692             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11693                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11694             }else{
11695                 dir = f.sortDir;
11696             }
11697         }
11698         this.sortToggle[f.name] = dir;
11699         this.sortInfo = {field: f.name, direction: dir};
11700         if(!this.remoteSort){
11701             this.applySort();
11702             this.fireEvent("datachanged", this);
11703         }else{
11704             this.load(this.lastOptions);
11705         }
11706     },
11707
11708     /**
11709      * Calls the specified function for each of the Records in the cache.
11710      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11711      * Returning <em>false</em> aborts and exits the iteration.
11712      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11713      */
11714     each : function(fn, scope){
11715         this.data.each(fn, scope);
11716     },
11717
11718     /**
11719      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11720      * (e.g., during paging).
11721      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11722      */
11723     getModifiedRecords : function(){
11724         return this.modified;
11725     },
11726
11727     // private
11728     createFilterFn : function(property, value, anyMatch){
11729         if(!value.exec){ // not a regex
11730             value = String(value);
11731             if(value.length == 0){
11732                 return false;
11733             }
11734             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11735         }
11736         return function(r){
11737             return value.test(r.data[property]);
11738         };
11739     },
11740
11741     /**
11742      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11743      * @param {String} property A field on your records
11744      * @param {Number} start The record index to start at (defaults to 0)
11745      * @param {Number} end The last record index to include (defaults to length - 1)
11746      * @return {Number} The sum
11747      */
11748     sum : function(property, start, end){
11749         var rs = this.data.items, v = 0;
11750         start = start || 0;
11751         end = (end || end === 0) ? end : rs.length-1;
11752
11753         for(var i = start; i <= end; i++){
11754             v += (rs[i].data[property] || 0);
11755         }
11756         return v;
11757     },
11758
11759     /**
11760      * Filter the records by a specified property.
11761      * @param {String} field A field on your records
11762      * @param {String/RegExp} value Either a string that the field
11763      * should start with or a RegExp to test against the field
11764      * @param {Boolean} anyMatch True to match any part not just the beginning
11765      */
11766     filter : function(property, value, anyMatch){
11767         var fn = this.createFilterFn(property, value, anyMatch);
11768         return fn ? this.filterBy(fn) : this.clearFilter();
11769     },
11770
11771     /**
11772      * Filter by a function. The specified function will be called with each
11773      * record in this data source. If the function returns true the record is included,
11774      * otherwise it is filtered.
11775      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11776      * @param {Object} scope (optional) The scope of the function (defaults to this)
11777      */
11778     filterBy : function(fn, scope){
11779         this.snapshot = this.snapshot || this.data;
11780         this.data = this.queryBy(fn, scope||this);
11781         this.fireEvent("datachanged", this);
11782     },
11783
11784     /**
11785      * Query the records by a specified property.
11786      * @param {String} field A field on your records
11787      * @param {String/RegExp} value Either a string that the field
11788      * should start with or a RegExp to test against the field
11789      * @param {Boolean} anyMatch True to match any part not just the beginning
11790      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11791      */
11792     query : function(property, value, anyMatch){
11793         var fn = this.createFilterFn(property, value, anyMatch);
11794         return fn ? this.queryBy(fn) : this.data.clone();
11795     },
11796
11797     /**
11798      * Query by a function. The specified function will be called with each
11799      * record in this data source. If the function returns true the record is included
11800      * in the results.
11801      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11802      * @param {Object} scope (optional) The scope of the function (defaults to this)
11803       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11804      **/
11805     queryBy : function(fn, scope){
11806         var data = this.snapshot || this.data;
11807         return data.filterBy(fn, scope||this);
11808     },
11809
11810     /**
11811      * Collects unique values for a particular dataIndex from this store.
11812      * @param {String} dataIndex The property to collect
11813      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11814      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11815      * @return {Array} An array of the unique values
11816      **/
11817     collect : function(dataIndex, allowNull, bypassFilter){
11818         var d = (bypassFilter === true && this.snapshot) ?
11819                 this.snapshot.items : this.data.items;
11820         var v, sv, r = [], l = {};
11821         for(var i = 0, len = d.length; i < len; i++){
11822             v = d[i].data[dataIndex];
11823             sv = String(v);
11824             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11825                 l[sv] = true;
11826                 r[r.length] = v;
11827             }
11828         }
11829         return r;
11830     },
11831
11832     /**
11833      * Revert to a view of the Record cache with no filtering applied.
11834      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11835      */
11836     clearFilter : function(suppressEvent){
11837         if(this.snapshot && this.snapshot != this.data){
11838             this.data = this.snapshot;
11839             delete this.snapshot;
11840             if(suppressEvent !== true){
11841                 this.fireEvent("datachanged", this);
11842             }
11843         }
11844     },
11845
11846     // private
11847     afterEdit : function(record){
11848         if(this.modified.indexOf(record) == -1){
11849             this.modified.push(record);
11850         }
11851         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11852     },
11853     
11854     // private
11855     afterReject : function(record){
11856         this.modified.remove(record);
11857         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11858     },
11859
11860     // private
11861     afterCommit : function(record){
11862         this.modified.remove(record);
11863         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11864     },
11865
11866     /**
11867      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11868      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11869      */
11870     commitChanges : function(){
11871         var m = this.modified.slice(0);
11872         this.modified = [];
11873         for(var i = 0, len = m.length; i < len; i++){
11874             m[i].commit();
11875         }
11876     },
11877
11878     /**
11879      * Cancel outstanding changes on all changed records.
11880      */
11881     rejectChanges : function(){
11882         var m = this.modified.slice(0);
11883         this.modified = [];
11884         for(var i = 0, len = m.length; i < len; i++){
11885             m[i].reject();
11886         }
11887     },
11888
11889     onMetaChange : function(meta, rtype, o){
11890         this.recordType = rtype;
11891         this.fields = rtype.prototype.fields;
11892         delete this.snapshot;
11893         this.sortInfo = meta.sortInfo || this.sortInfo;
11894         this.modified = [];
11895         this.fireEvent('metachange', this, this.reader.meta);
11896     },
11897     
11898     moveIndex : function(data, type)
11899     {
11900         var index = this.indexOf(data);
11901         
11902         var newIndex = index + type;
11903         
11904         this.remove(data);
11905         
11906         this.insert(newIndex, data);
11907         
11908     }
11909 });/*
11910  * Based on:
11911  * Ext JS Library 1.1.1
11912  * Copyright(c) 2006-2007, Ext JS, LLC.
11913  *
11914  * Originally Released Under LGPL - original licence link has changed is not relivant.
11915  *
11916  * Fork - LGPL
11917  * <script type="text/javascript">
11918  */
11919
11920 /**
11921  * @class Roo.data.SimpleStore
11922  * @extends Roo.data.Store
11923  * Small helper class to make creating Stores from Array data easier.
11924  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11925  * @cfg {Array} fields An array of field definition objects, or field name strings.
11926  * @cfg {Array} data The multi-dimensional array of data
11927  * @constructor
11928  * @param {Object} config
11929  */
11930 Roo.data.SimpleStore = function(config){
11931     Roo.data.SimpleStore.superclass.constructor.call(this, {
11932         isLocal : true,
11933         reader: new Roo.data.ArrayReader({
11934                 id: config.id
11935             },
11936             Roo.data.Record.create(config.fields)
11937         ),
11938         proxy : new Roo.data.MemoryProxy(config.data)
11939     });
11940     this.load();
11941 };
11942 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11943  * Based on:
11944  * Ext JS Library 1.1.1
11945  * Copyright(c) 2006-2007, Ext JS, LLC.
11946  *
11947  * Originally Released Under LGPL - original licence link has changed is not relivant.
11948  *
11949  * Fork - LGPL
11950  * <script type="text/javascript">
11951  */
11952
11953 /**
11954 /**
11955  * @extends Roo.data.Store
11956  * @class Roo.data.JsonStore
11957  * Small helper class to make creating Stores for JSON data easier. <br/>
11958 <pre><code>
11959 var store = new Roo.data.JsonStore({
11960     url: 'get-images.php',
11961     root: 'images',
11962     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11963 });
11964 </code></pre>
11965  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11966  * JsonReader and HttpProxy (unless inline data is provided).</b>
11967  * @cfg {Array} fields An array of field definition objects, or field name strings.
11968  * @constructor
11969  * @param {Object} config
11970  */
11971 Roo.data.JsonStore = function(c){
11972     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11973         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11974         reader: new Roo.data.JsonReader(c, c.fields)
11975     }));
11976 };
11977 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11978  * Based on:
11979  * Ext JS Library 1.1.1
11980  * Copyright(c) 2006-2007, Ext JS, LLC.
11981  *
11982  * Originally Released Under LGPL - original licence link has changed is not relivant.
11983  *
11984  * Fork - LGPL
11985  * <script type="text/javascript">
11986  */
11987
11988  
11989 Roo.data.Field = function(config){
11990     if(typeof config == "string"){
11991         config = {name: config};
11992     }
11993     Roo.apply(this, config);
11994     
11995     if(!this.type){
11996         this.type = "auto";
11997     }
11998     
11999     var st = Roo.data.SortTypes;
12000     // named sortTypes are supported, here we look them up
12001     if(typeof this.sortType == "string"){
12002         this.sortType = st[this.sortType];
12003     }
12004     
12005     // set default sortType for strings and dates
12006     if(!this.sortType){
12007         switch(this.type){
12008             case "string":
12009                 this.sortType = st.asUCString;
12010                 break;
12011             case "date":
12012                 this.sortType = st.asDate;
12013                 break;
12014             default:
12015                 this.sortType = st.none;
12016         }
12017     }
12018
12019     // define once
12020     var stripRe = /[\$,%]/g;
12021
12022     // prebuilt conversion function for this field, instead of
12023     // switching every time we're reading a value
12024     if(!this.convert){
12025         var cv, dateFormat = this.dateFormat;
12026         switch(this.type){
12027             case "":
12028             case "auto":
12029             case undefined:
12030                 cv = function(v){ return v; };
12031                 break;
12032             case "string":
12033                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12034                 break;
12035             case "int":
12036                 cv = function(v){
12037                     return v !== undefined && v !== null && v !== '' ?
12038                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12039                     };
12040                 break;
12041             case "float":
12042                 cv = function(v){
12043                     return v !== undefined && v !== null && v !== '' ?
12044                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12045                     };
12046                 break;
12047             case "bool":
12048             case "boolean":
12049                 cv = function(v){ return v === true || v === "true" || v == 1; };
12050                 break;
12051             case "date":
12052                 cv = function(v){
12053                     if(!v){
12054                         return '';
12055                     }
12056                     if(v instanceof Date){
12057                         return v;
12058                     }
12059                     if(dateFormat){
12060                         if(dateFormat == "timestamp"){
12061                             return new Date(v*1000);
12062                         }
12063                         return Date.parseDate(v, dateFormat);
12064                     }
12065                     var parsed = Date.parse(v);
12066                     return parsed ? new Date(parsed) : null;
12067                 };
12068              break;
12069             
12070         }
12071         this.convert = cv;
12072     }
12073 };
12074
12075 Roo.data.Field.prototype = {
12076     dateFormat: null,
12077     defaultValue: "",
12078     mapping: null,
12079     sortType : null,
12080     sortDir : "ASC"
12081 };/*
12082  * Based on:
12083  * Ext JS Library 1.1.1
12084  * Copyright(c) 2006-2007, Ext JS, LLC.
12085  *
12086  * Originally Released Under LGPL - original licence link has changed is not relivant.
12087  *
12088  * Fork - LGPL
12089  * <script type="text/javascript">
12090  */
12091  
12092 // Base class for reading structured data from a data source.  This class is intended to be
12093 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12094
12095 /**
12096  * @class Roo.data.DataReader
12097  * Base class for reading structured data from a data source.  This class is intended to be
12098  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12099  */
12100
12101 Roo.data.DataReader = function(meta, recordType){
12102     
12103     this.meta = meta;
12104     
12105     this.recordType = recordType instanceof Array ? 
12106         Roo.data.Record.create(recordType) : recordType;
12107 };
12108
12109 Roo.data.DataReader.prototype = {
12110      /**
12111      * Create an empty record
12112      * @param {Object} data (optional) - overlay some values
12113      * @return {Roo.data.Record} record created.
12114      */
12115     newRow :  function(d) {
12116         var da =  {};
12117         this.recordType.prototype.fields.each(function(c) {
12118             switch( c.type) {
12119                 case 'int' : da[c.name] = 0; break;
12120                 case 'date' : da[c.name] = new Date(); break;
12121                 case 'float' : da[c.name] = 0.0; break;
12122                 case 'boolean' : da[c.name] = false; break;
12123                 default : da[c.name] = ""; break;
12124             }
12125             
12126         });
12127         return new this.recordType(Roo.apply(da, d));
12128     }
12129     
12130 };/*
12131  * Based on:
12132  * Ext JS Library 1.1.1
12133  * Copyright(c) 2006-2007, Ext JS, LLC.
12134  *
12135  * Originally Released Under LGPL - original licence link has changed is not relivant.
12136  *
12137  * Fork - LGPL
12138  * <script type="text/javascript">
12139  */
12140
12141 /**
12142  * @class Roo.data.DataProxy
12143  * @extends Roo.data.Observable
12144  * This class is an abstract base class for implementations which provide retrieval of
12145  * unformatted data objects.<br>
12146  * <p>
12147  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12148  * (of the appropriate type which knows how to parse the data object) to provide a block of
12149  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12150  * <p>
12151  * Custom implementations must implement the load method as described in
12152  * {@link Roo.data.HttpProxy#load}.
12153  */
12154 Roo.data.DataProxy = function(){
12155     this.addEvents({
12156         /**
12157          * @event beforeload
12158          * Fires before a network request is made to retrieve a data object.
12159          * @param {Object} This DataProxy object.
12160          * @param {Object} params The params parameter to the load function.
12161          */
12162         beforeload : true,
12163         /**
12164          * @event load
12165          * Fires before the load method's callback is called.
12166          * @param {Object} This DataProxy object.
12167          * @param {Object} o The data object.
12168          * @param {Object} arg The callback argument object passed to the load function.
12169          */
12170         load : true,
12171         /**
12172          * @event loadexception
12173          * Fires if an Exception occurs during data retrieval.
12174          * @param {Object} This DataProxy object.
12175          * @param {Object} o The data object.
12176          * @param {Object} arg The callback argument object passed to the load function.
12177          * @param {Object} e The Exception.
12178          */
12179         loadexception : true
12180     });
12181     Roo.data.DataProxy.superclass.constructor.call(this);
12182 };
12183
12184 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12185
12186     /**
12187      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12188      */
12189 /*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199 /**
12200  * @class Roo.data.MemoryProxy
12201  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12202  * to the Reader when its load method is called.
12203  * @constructor
12204  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12205  */
12206 Roo.data.MemoryProxy = function(data){
12207     if (data.data) {
12208         data = data.data;
12209     }
12210     Roo.data.MemoryProxy.superclass.constructor.call(this);
12211     this.data = data;
12212 };
12213
12214 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12215     
12216     /**
12217      * Load data from the requested source (in this case an in-memory
12218      * data object passed to the constructor), read the data object into
12219      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12220      * process that block using the passed callback.
12221      * @param {Object} params This parameter is not used by the MemoryProxy class.
12222      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12223      * object into a block of Roo.data.Records.
12224      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12225      * The function must be passed <ul>
12226      * <li>The Record block object</li>
12227      * <li>The "arg" argument from the load function</li>
12228      * <li>A boolean success indicator</li>
12229      * </ul>
12230      * @param {Object} scope The scope in which to call the callback
12231      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12232      */
12233     load : function(params, reader, callback, scope, arg){
12234         params = params || {};
12235         var result;
12236         try {
12237             result = reader.readRecords(this.data);
12238         }catch(e){
12239             this.fireEvent("loadexception", this, arg, null, e);
12240             callback.call(scope, null, arg, false);
12241             return;
12242         }
12243         callback.call(scope, result, arg, true);
12244     },
12245     
12246     // private
12247     update : function(params, records){
12248         
12249     }
12250 });/*
12251  * Based on:
12252  * Ext JS Library 1.1.1
12253  * Copyright(c) 2006-2007, Ext JS, LLC.
12254  *
12255  * Originally Released Under LGPL - original licence link has changed is not relivant.
12256  *
12257  * Fork - LGPL
12258  * <script type="text/javascript">
12259  */
12260 /**
12261  * @class Roo.data.HttpProxy
12262  * @extends Roo.data.DataProxy
12263  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12264  * configured to reference a certain URL.<br><br>
12265  * <p>
12266  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12267  * from which the running page was served.<br><br>
12268  * <p>
12269  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12270  * <p>
12271  * Be aware that to enable the browser to parse an XML document, the server must set
12272  * the Content-Type header in the HTTP response to "text/xml".
12273  * @constructor
12274  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12275  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12276  * will be used to make the request.
12277  */
12278 Roo.data.HttpProxy = function(conn){
12279     Roo.data.HttpProxy.superclass.constructor.call(this);
12280     // is conn a conn config or a real conn?
12281     this.conn = conn;
12282     this.useAjax = !conn || !conn.events;
12283   
12284 };
12285
12286 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12287     // thse are take from connection...
12288     
12289     /**
12290      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12291      */
12292     /**
12293      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12294      * extra parameters to each request made by this object. (defaults to undefined)
12295      */
12296     /**
12297      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12298      *  to each request made by this object. (defaults to undefined)
12299      */
12300     /**
12301      * @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)
12302      */
12303     /**
12304      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12305      */
12306      /**
12307      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12308      * @type Boolean
12309      */
12310   
12311
12312     /**
12313      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12314      * @type Boolean
12315      */
12316     /**
12317      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12318      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12319      * a finer-grained basis than the DataProxy events.
12320      */
12321     getConnection : function(){
12322         return this.useAjax ? Roo.Ajax : this.conn;
12323     },
12324
12325     /**
12326      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12327      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12328      * process that block using the passed callback.
12329      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12330      * for the request to the remote server.
12331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12332      * object into a block of Roo.data.Records.
12333      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12334      * The function must be passed <ul>
12335      * <li>The Record block object</li>
12336      * <li>The "arg" argument from the load function</li>
12337      * <li>A boolean success indicator</li>
12338      * </ul>
12339      * @param {Object} scope The scope in which to call the callback
12340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12341      */
12342     load : function(params, reader, callback, scope, arg){
12343         if(this.fireEvent("beforeload", this, params) !== false){
12344             var  o = {
12345                 params : params || {},
12346                 request: {
12347                     callback : callback,
12348                     scope : scope,
12349                     arg : arg
12350                 },
12351                 reader: reader,
12352                 callback : this.loadResponse,
12353                 scope: this
12354             };
12355             if(this.useAjax){
12356                 Roo.applyIf(o, this.conn);
12357                 if(this.activeRequest){
12358                     Roo.Ajax.abort(this.activeRequest);
12359                 }
12360                 this.activeRequest = Roo.Ajax.request(o);
12361             }else{
12362                 this.conn.request(o);
12363             }
12364         }else{
12365             callback.call(scope||this, null, arg, false);
12366         }
12367     },
12368
12369     // private
12370     loadResponse : function(o, success, response){
12371         delete this.activeRequest;
12372         if(!success){
12373             this.fireEvent("loadexception", this, o, response);
12374             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12375             return;
12376         }
12377         var result;
12378         try {
12379             result = o.reader.read(response);
12380         }catch(e){
12381             this.fireEvent("loadexception", this, o, response, e);
12382             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12383             return;
12384         }
12385         
12386         this.fireEvent("load", this, o, o.request.arg);
12387         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12388     },
12389
12390     // private
12391     update : function(dataSet){
12392
12393     },
12394
12395     // private
12396     updateResponse : function(dataSet){
12397
12398     }
12399 });/*
12400  * Based on:
12401  * Ext JS Library 1.1.1
12402  * Copyright(c) 2006-2007, Ext JS, LLC.
12403  *
12404  * Originally Released Under LGPL - original licence link has changed is not relivant.
12405  *
12406  * Fork - LGPL
12407  * <script type="text/javascript">
12408  */
12409
12410 /**
12411  * @class Roo.data.ScriptTagProxy
12412  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12413  * other than the originating domain of the running page.<br><br>
12414  * <p>
12415  * <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
12416  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12417  * <p>
12418  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12419  * source code that is used as the source inside a &lt;script> tag.<br><br>
12420  * <p>
12421  * In order for the browser to process the returned data, the server must wrap the data object
12422  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12423  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12424  * depending on whether the callback name was passed:
12425  * <p>
12426  * <pre><code>
12427 boolean scriptTag = false;
12428 String cb = request.getParameter("callback");
12429 if (cb != null) {
12430     scriptTag = true;
12431     response.setContentType("text/javascript");
12432 } else {
12433     response.setContentType("application/x-json");
12434 }
12435 Writer out = response.getWriter();
12436 if (scriptTag) {
12437     out.write(cb + "(");
12438 }
12439 out.print(dataBlock.toJsonString());
12440 if (scriptTag) {
12441     out.write(");");
12442 }
12443 </pre></code>
12444  *
12445  * @constructor
12446  * @param {Object} config A configuration object.
12447  */
12448 Roo.data.ScriptTagProxy = function(config){
12449     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12450     Roo.apply(this, config);
12451     this.head = document.getElementsByTagName("head")[0];
12452 };
12453
12454 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12455
12456 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12457     /**
12458      * @cfg {String} url The URL from which to request the data object.
12459      */
12460     /**
12461      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12462      */
12463     timeout : 30000,
12464     /**
12465      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12466      * the server the name of the callback function set up by the load call to process the returned data object.
12467      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12468      * javascript output which calls this named function passing the data object as its only parameter.
12469      */
12470     callbackParam : "callback",
12471     /**
12472      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12473      * name to the request.
12474      */
12475     nocache : true,
12476
12477     /**
12478      * Load data from the configured URL, read the data object into
12479      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12480      * process that block using the passed callback.
12481      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12482      * for the request to the remote server.
12483      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12484      * object into a block of Roo.data.Records.
12485      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12486      * The function must be passed <ul>
12487      * <li>The Record block object</li>
12488      * <li>The "arg" argument from the load function</li>
12489      * <li>A boolean success indicator</li>
12490      * </ul>
12491      * @param {Object} scope The scope in which to call the callback
12492      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12493      */
12494     load : function(params, reader, callback, scope, arg){
12495         if(this.fireEvent("beforeload", this, params) !== false){
12496
12497             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12498
12499             var url = this.url;
12500             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12501             if(this.nocache){
12502                 url += "&_dc=" + (new Date().getTime());
12503             }
12504             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12505             var trans = {
12506                 id : transId,
12507                 cb : "stcCallback"+transId,
12508                 scriptId : "stcScript"+transId,
12509                 params : params,
12510                 arg : arg,
12511                 url : url,
12512                 callback : callback,
12513                 scope : scope,
12514                 reader : reader
12515             };
12516             var conn = this;
12517
12518             window[trans.cb] = function(o){
12519                 conn.handleResponse(o, trans);
12520             };
12521
12522             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12523
12524             if(this.autoAbort !== false){
12525                 this.abort();
12526             }
12527
12528             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12529
12530             var script = document.createElement("script");
12531             script.setAttribute("src", url);
12532             script.setAttribute("type", "text/javascript");
12533             script.setAttribute("id", trans.scriptId);
12534             this.head.appendChild(script);
12535
12536             this.trans = trans;
12537         }else{
12538             callback.call(scope||this, null, arg, false);
12539         }
12540     },
12541
12542     // private
12543     isLoading : function(){
12544         return this.trans ? true : false;
12545     },
12546
12547     /**
12548      * Abort the current server request.
12549      */
12550     abort : function(){
12551         if(this.isLoading()){
12552             this.destroyTrans(this.trans);
12553         }
12554     },
12555
12556     // private
12557     destroyTrans : function(trans, isLoaded){
12558         this.head.removeChild(document.getElementById(trans.scriptId));
12559         clearTimeout(trans.timeoutId);
12560         if(isLoaded){
12561             window[trans.cb] = undefined;
12562             try{
12563                 delete window[trans.cb];
12564             }catch(e){}
12565         }else{
12566             // if hasn't been loaded, wait for load to remove it to prevent script error
12567             window[trans.cb] = function(){
12568                 window[trans.cb] = undefined;
12569                 try{
12570                     delete window[trans.cb];
12571                 }catch(e){}
12572             };
12573         }
12574     },
12575
12576     // private
12577     handleResponse : function(o, trans){
12578         this.trans = false;
12579         this.destroyTrans(trans, true);
12580         var result;
12581         try {
12582             result = trans.reader.readRecords(o);
12583         }catch(e){
12584             this.fireEvent("loadexception", this, o, trans.arg, e);
12585             trans.callback.call(trans.scope||window, null, trans.arg, false);
12586             return;
12587         }
12588         this.fireEvent("load", this, o, trans.arg);
12589         trans.callback.call(trans.scope||window, result, trans.arg, true);
12590     },
12591
12592     // private
12593     handleFailure : function(trans){
12594         this.trans = false;
12595         this.destroyTrans(trans, false);
12596         this.fireEvent("loadexception", this, null, trans.arg);
12597         trans.callback.call(trans.scope||window, null, trans.arg, false);
12598     }
12599 });/*
12600  * Based on:
12601  * Ext JS Library 1.1.1
12602  * Copyright(c) 2006-2007, Ext JS, LLC.
12603  *
12604  * Originally Released Under LGPL - original licence link has changed is not relivant.
12605  *
12606  * Fork - LGPL
12607  * <script type="text/javascript">
12608  */
12609
12610 /**
12611  * @class Roo.data.JsonReader
12612  * @extends Roo.data.DataReader
12613  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12614  * based on mappings in a provided Roo.data.Record constructor.
12615  * 
12616  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12617  * in the reply previously. 
12618  * 
12619  * <p>
12620  * Example code:
12621  * <pre><code>
12622 var RecordDef = Roo.data.Record.create([
12623     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12624     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12625 ]);
12626 var myReader = new Roo.data.JsonReader({
12627     totalProperty: "results",    // The property which contains the total dataset size (optional)
12628     root: "rows",                // The property which contains an Array of row objects
12629     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12630 }, RecordDef);
12631 </code></pre>
12632  * <p>
12633  * This would consume a JSON file like this:
12634  * <pre><code>
12635 { 'results': 2, 'rows': [
12636     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12637     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12638 }
12639 </code></pre>
12640  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12641  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12642  * paged from the remote server.
12643  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12644  * @cfg {String} root name of the property which contains the Array of row objects.
12645  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12646  * @cfg {Array} fields Array of field definition objects
12647  * @constructor
12648  * Create a new JsonReader
12649  * @param {Object} meta Metadata configuration options
12650  * @param {Object} recordType Either an Array of field definition objects,
12651  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12652  */
12653 Roo.data.JsonReader = function(meta, recordType){
12654     
12655     meta = meta || {};
12656     // set some defaults:
12657     Roo.applyIf(meta, {
12658         totalProperty: 'total',
12659         successProperty : 'success',
12660         root : 'data',
12661         id : 'id'
12662     });
12663     
12664     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12665 };
12666 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12667     
12668     /**
12669      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12670      * Used by Store query builder to append _requestMeta to params.
12671      * 
12672      */
12673     metaFromRemote : false,
12674     /**
12675      * This method is only used by a DataProxy which has retrieved data from a remote server.
12676      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12677      * @return {Object} data A data block which is used by an Roo.data.Store object as
12678      * a cache of Roo.data.Records.
12679      */
12680     read : function(response){
12681         var json = response.responseText;
12682        
12683         var o = /* eval:var:o */ eval("("+json+")");
12684         if(!o) {
12685             throw {message: "JsonReader.read: Json object not found"};
12686         }
12687         
12688         if(o.metaData){
12689             
12690             delete this.ef;
12691             this.metaFromRemote = true;
12692             this.meta = o.metaData;
12693             this.recordType = Roo.data.Record.create(o.metaData.fields);
12694             this.onMetaChange(this.meta, this.recordType, o);
12695         }
12696         return this.readRecords(o);
12697     },
12698
12699     // private function a store will implement
12700     onMetaChange : function(meta, recordType, o){
12701
12702     },
12703
12704     /**
12705          * @ignore
12706          */
12707     simpleAccess: function(obj, subsc) {
12708         return obj[subsc];
12709     },
12710
12711         /**
12712          * @ignore
12713          */
12714     getJsonAccessor: function(){
12715         var re = /[\[\.]/;
12716         return function(expr) {
12717             try {
12718                 return(re.test(expr))
12719                     ? new Function("obj", "return obj." + expr)
12720                     : function(obj){
12721                         return obj[expr];
12722                     };
12723             } catch(e){}
12724             return Roo.emptyFn;
12725         };
12726     }(),
12727
12728     /**
12729      * Create a data block containing Roo.data.Records from an XML document.
12730      * @param {Object} o An object which contains an Array of row objects in the property specified
12731      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12732      * which contains the total size of the dataset.
12733      * @return {Object} data A data block which is used by an Roo.data.Store object as
12734      * a cache of Roo.data.Records.
12735      */
12736     readRecords : function(o){
12737         /**
12738          * After any data loads, the raw JSON data is available for further custom processing.
12739          * @type Object
12740          */
12741         this.o = o;
12742         var s = this.meta, Record = this.recordType,
12743             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12744
12745 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12746         if (!this.ef) {
12747             if(s.totalProperty) {
12748                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12749                 }
12750                 if(s.successProperty) {
12751                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12752                 }
12753                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12754                 if (s.id) {
12755                         var g = this.getJsonAccessor(s.id);
12756                         this.getId = function(rec) {
12757                                 var r = g(rec);  
12758                                 return (r === undefined || r === "") ? null : r;
12759                         };
12760                 } else {
12761                         this.getId = function(){return null;};
12762                 }
12763             this.ef = [];
12764             for(var jj = 0; jj < fl; jj++){
12765                 f = fi[jj];
12766                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12767                 this.ef[jj] = this.getJsonAccessor(map);
12768             }
12769         }
12770
12771         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12772         if(s.totalProperty){
12773             var vt = parseInt(this.getTotal(o), 10);
12774             if(!isNaN(vt)){
12775                 totalRecords = vt;
12776             }
12777         }
12778         if(s.successProperty){
12779             var vs = this.getSuccess(o);
12780             if(vs === false || vs === 'false'){
12781                 success = false;
12782             }
12783         }
12784         var records = [];
12785         for(var i = 0; i < c; i++){
12786                 var n = root[i];
12787             var values = {};
12788             var id = this.getId(n);
12789             for(var j = 0; j < fl; j++){
12790                 f = fi[j];
12791             var v = this.ef[j](n);
12792             if (!f.convert) {
12793                 Roo.log('missing convert for ' + f.name);
12794                 Roo.log(f);
12795                 continue;
12796             }
12797             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12798             }
12799             var record = new Record(values, id);
12800             record.json = n;
12801             records[i] = record;
12802         }
12803         return {
12804             raw : o,
12805             success : success,
12806             records : records,
12807             totalRecords : totalRecords
12808         };
12809     }
12810 });/*
12811  * Based on:
12812  * Ext JS Library 1.1.1
12813  * Copyright(c) 2006-2007, Ext JS, LLC.
12814  *
12815  * Originally Released Under LGPL - original licence link has changed is not relivant.
12816  *
12817  * Fork - LGPL
12818  * <script type="text/javascript">
12819  */
12820
12821 /**
12822  * @class Roo.data.ArrayReader
12823  * @extends Roo.data.DataReader
12824  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12825  * Each element of that Array represents a row of data fields. The
12826  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12827  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12828  * <p>
12829  * Example code:.
12830  * <pre><code>
12831 var RecordDef = Roo.data.Record.create([
12832     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12833     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12834 ]);
12835 var myReader = new Roo.data.ArrayReader({
12836     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12837 }, RecordDef);
12838 </code></pre>
12839  * <p>
12840  * This would consume an Array like this:
12841  * <pre><code>
12842 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12843   </code></pre>
12844  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12845  * @constructor
12846  * Create a new JsonReader
12847  * @param {Object} meta Metadata configuration options.
12848  * @param {Object} recordType Either an Array of field definition objects
12849  * as specified to {@link Roo.data.Record#create},
12850  * or an {@link Roo.data.Record} object
12851  * created using {@link Roo.data.Record#create}.
12852  */
12853 Roo.data.ArrayReader = function(meta, recordType){
12854     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12855 };
12856
12857 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12858     /**
12859      * Create a data block containing Roo.data.Records from an XML document.
12860      * @param {Object} o An Array of row objects which represents the dataset.
12861      * @return {Object} data A data block which is used by an Roo.data.Store object as
12862      * a cache of Roo.data.Records.
12863      */
12864     readRecords : function(o){
12865         var sid = this.meta ? this.meta.id : null;
12866         var recordType = this.recordType, fields = recordType.prototype.fields;
12867         var records = [];
12868         var root = o;
12869             for(var i = 0; i < root.length; i++){
12870                     var n = root[i];
12871                 var values = {};
12872                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12873                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12874                 var f = fields.items[j];
12875                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12876                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12877                 v = f.convert(v);
12878                 values[f.name] = v;
12879             }
12880                 var record = new recordType(values, id);
12881                 record.json = n;
12882                 records[records.length] = record;
12883             }
12884             return {
12885                 records : records,
12886                 totalRecords : records.length
12887             };
12888     }
12889 });/*
12890  * - LGPL
12891  * * 
12892  */
12893
12894 /**
12895  * @class Roo.bootstrap.ComboBox
12896  * @extends Roo.bootstrap.TriggerField
12897  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12898  * @cfg {Boolean} append (true|false) default false
12899  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12900  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12901  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12902  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12903  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12904  * @cfg {Boolean} animate default true
12905  * @cfg {Boolean} emptyResultText only for touch device
12906  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12907  * @cfg {String} emptyTitle default ''
12908  * @constructor
12909  * Create a new ComboBox.
12910  * @param {Object} config Configuration options
12911  */
12912 Roo.bootstrap.ComboBox = function(config){
12913     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12914     this.addEvents({
12915         /**
12916          * @event expand
12917          * Fires when the dropdown list is expanded
12918         * @param {Roo.bootstrap.ComboBox} combo This combo box
12919         */
12920         'expand' : true,
12921         /**
12922          * @event collapse
12923          * Fires when the dropdown list is collapsed
12924         * @param {Roo.bootstrap.ComboBox} combo This combo box
12925         */
12926         'collapse' : true,
12927         /**
12928          * @event beforeselect
12929          * Fires before a list item is selected. Return false to cancel the selection.
12930         * @param {Roo.bootstrap.ComboBox} combo This combo box
12931         * @param {Roo.data.Record} record The data record returned from the underlying store
12932         * @param {Number} index The index of the selected item in the dropdown list
12933         */
12934         'beforeselect' : true,
12935         /**
12936          * @event select
12937          * Fires when a list item is selected
12938         * @param {Roo.bootstrap.ComboBox} combo This combo box
12939         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12940         * @param {Number} index The index of the selected item in the dropdown list
12941         */
12942         'select' : true,
12943         /**
12944          * @event beforequery
12945          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12946          * The event object passed has these properties:
12947         * @param {Roo.bootstrap.ComboBox} combo This combo box
12948         * @param {String} query The query
12949         * @param {Boolean} forceAll true to force "all" query
12950         * @param {Boolean} cancel true to cancel the query
12951         * @param {Object} e The query event object
12952         */
12953         'beforequery': true,
12954          /**
12955          * @event add
12956          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12957         * @param {Roo.bootstrap.ComboBox} combo This combo box
12958         */
12959         'add' : true,
12960         /**
12961          * @event edit
12962          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12963         * @param {Roo.bootstrap.ComboBox} combo This combo box
12964         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12965         */
12966         'edit' : true,
12967         /**
12968          * @event remove
12969          * Fires when the remove value from the combobox array
12970         * @param {Roo.bootstrap.ComboBox} combo This combo box
12971         */
12972         'remove' : true,
12973         /**
12974          * @event afterremove
12975          * Fires when the remove value from the combobox array
12976         * @param {Roo.bootstrap.ComboBox} combo This combo box
12977         */
12978         'afterremove' : true,
12979         /**
12980          * @event specialfilter
12981          * Fires when specialfilter
12982             * @param {Roo.bootstrap.ComboBox} combo This combo box
12983             */
12984         'specialfilter' : true,
12985         /**
12986          * @event tick
12987          * Fires when tick the element
12988             * @param {Roo.bootstrap.ComboBox} combo This combo box
12989             */
12990         'tick' : true,
12991         /**
12992          * @event touchviewdisplay
12993          * Fires when touch view require special display (default is using displayField)
12994             * @param {Roo.bootstrap.ComboBox} combo This combo box
12995             * @param {Object} cfg set html .
12996             */
12997         'touchviewdisplay' : true
12998         
12999     });
13000     
13001     this.item = [];
13002     this.tickItems = [];
13003     
13004     this.selectedIndex = -1;
13005     if(this.mode == 'local'){
13006         if(config.queryDelay === undefined){
13007             this.queryDelay = 10;
13008         }
13009         if(config.minChars === undefined){
13010             this.minChars = 0;
13011         }
13012     }
13013 };
13014
13015 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13016      
13017     /**
13018      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13019      * rendering into an Roo.Editor, defaults to false)
13020      */
13021     /**
13022      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13023      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13024      */
13025     /**
13026      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13027      */
13028     /**
13029      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13030      * the dropdown list (defaults to undefined, with no header element)
13031      */
13032
13033      /**
13034      * @cfg {String/Roo.Template} tpl The template to use to render the output
13035      */
13036      
13037      /**
13038      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13039      */
13040     listWidth: undefined,
13041     /**
13042      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13043      * mode = 'remote' or 'text' if mode = 'local')
13044      */
13045     displayField: undefined,
13046     
13047     /**
13048      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13049      * mode = 'remote' or 'value' if mode = 'local'). 
13050      * Note: use of a valueField requires the user make a selection
13051      * in order for a value to be mapped.
13052      */
13053     valueField: undefined,
13054     /**
13055      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13056      */
13057     modalTitle : '',
13058     
13059     /**
13060      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13061      * field's data value (defaults to the underlying DOM element's name)
13062      */
13063     hiddenName: undefined,
13064     /**
13065      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13066      */
13067     listClass: '',
13068     /**
13069      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13070      */
13071     selectedClass: 'active',
13072     
13073     /**
13074      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13075      */
13076     shadow:'sides',
13077     /**
13078      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13079      * anchor positions (defaults to 'tl-bl')
13080      */
13081     listAlign: 'tl-bl?',
13082     /**
13083      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13084      */
13085     maxHeight: 300,
13086     /**
13087      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13088      * query specified by the allQuery config option (defaults to 'query')
13089      */
13090     triggerAction: 'query',
13091     /**
13092      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13093      * (defaults to 4, does not apply if editable = false)
13094      */
13095     minChars : 4,
13096     /**
13097      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13098      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13099      */
13100     typeAhead: false,
13101     /**
13102      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13103      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13104      */
13105     queryDelay: 500,
13106     /**
13107      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13108      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13109      */
13110     pageSize: 0,
13111     /**
13112      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13113      * when editable = true (defaults to false)
13114      */
13115     selectOnFocus:false,
13116     /**
13117      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13118      */
13119     queryParam: 'query',
13120     /**
13121      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13122      * when mode = 'remote' (defaults to 'Loading...')
13123      */
13124     loadingText: 'Loading...',
13125     /**
13126      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13127      */
13128     resizable: false,
13129     /**
13130      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13131      */
13132     handleHeight : 8,
13133     /**
13134      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13135      * traditional select (defaults to true)
13136      */
13137     editable: true,
13138     /**
13139      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13140      */
13141     allQuery: '',
13142     /**
13143      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13144      */
13145     mode: 'remote',
13146     /**
13147      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13148      * listWidth has a higher value)
13149      */
13150     minListWidth : 70,
13151     /**
13152      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13153      * allow the user to set arbitrary text into the field (defaults to false)
13154      */
13155     forceSelection:false,
13156     /**
13157      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13158      * if typeAhead = true (defaults to 250)
13159      */
13160     typeAheadDelay : 250,
13161     /**
13162      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13163      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13164      */
13165     valueNotFoundText : undefined,
13166     /**
13167      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13168      */
13169     blockFocus : false,
13170     
13171     /**
13172      * @cfg {Boolean} disableClear Disable showing of clear button.
13173      */
13174     disableClear : false,
13175     /**
13176      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13177      */
13178     alwaysQuery : false,
13179     
13180     /**
13181      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13182      */
13183     multiple : false,
13184     
13185     /**
13186      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13187      */
13188     invalidClass : "has-warning",
13189     
13190     /**
13191      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13192      */
13193     validClass : "has-success",
13194     
13195     /**
13196      * @cfg {Boolean} specialFilter (true|false) special filter default false
13197      */
13198     specialFilter : false,
13199     
13200     /**
13201      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13202      */
13203     mobileTouchView : true,
13204     
13205     /**
13206      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13207      */
13208     useNativeIOS : false,
13209     
13210     /**
13211      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13212      */
13213     mobile_restrict_height : false,
13214     
13215     ios_options : false,
13216     
13217     //private
13218     addicon : false,
13219     editicon: false,
13220     
13221     page: 0,
13222     hasQuery: false,
13223     append: false,
13224     loadNext: false,
13225     autoFocus : true,
13226     tickable : false,
13227     btnPosition : 'right',
13228     triggerList : true,
13229     showToggleBtn : true,
13230     animate : true,
13231     emptyResultText: 'Empty',
13232     triggerText : 'Select',
13233     emptyTitle : '',
13234     
13235     // element that contains real text value.. (when hidden is used..)
13236     
13237     getAutoCreate : function()
13238     {   
13239         var cfg = false;
13240         //render
13241         /*
13242          * Render classic select for iso
13243          */
13244         
13245         if(Roo.isIOS && this.useNativeIOS){
13246             cfg = this.getAutoCreateNativeIOS();
13247             return cfg;
13248         }
13249         
13250         /*
13251          * Touch Devices
13252          */
13253         
13254         if(Roo.isTouch && this.mobileTouchView){
13255             cfg = this.getAutoCreateTouchView();
13256             return cfg;;
13257         }
13258         
13259         /*
13260          *  Normal ComboBox
13261          */
13262         if(!this.tickable){
13263             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13264             return cfg;
13265         }
13266         
13267         /*
13268          *  ComboBox with tickable selections
13269          */
13270              
13271         var align = this.labelAlign || this.parentLabelAlign();
13272         
13273         cfg = {
13274             cls : 'form-group roo-combobox-tickable' //input-group
13275         };
13276         
13277         var btn_text_select = '';
13278         var btn_text_done = '';
13279         var btn_text_cancel = '';
13280         
13281         if (this.btn_text_show) {
13282             btn_text_select = 'Select';
13283             btn_text_done = 'Done';
13284             btn_text_cancel = 'Cancel'; 
13285         }
13286         
13287         var buttons = {
13288             tag : 'div',
13289             cls : 'tickable-buttons',
13290             cn : [
13291                 {
13292                     tag : 'button',
13293                     type : 'button',
13294                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13295                     //html : this.triggerText
13296                     html: btn_text_select
13297                 },
13298                 {
13299                     tag : 'button',
13300                     type : 'button',
13301                     name : 'ok',
13302                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13303                     //html : 'Done'
13304                     html: btn_text_done
13305                 },
13306                 {
13307                     tag : 'button',
13308                     type : 'button',
13309                     name : 'cancel',
13310                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13311                     //html : 'Cancel'
13312                     html: btn_text_cancel
13313                 }
13314             ]
13315         };
13316         
13317         if(this.editable){
13318             buttons.cn.unshift({
13319                 tag: 'input',
13320                 cls: 'roo-select2-search-field-input'
13321             });
13322         }
13323         
13324         var _this = this;
13325         
13326         Roo.each(buttons.cn, function(c){
13327             if (_this.size) {
13328                 c.cls += ' btn-' + _this.size;
13329             }
13330
13331             if (_this.disabled) {
13332                 c.disabled = true;
13333             }
13334         });
13335         
13336         var box = {
13337             tag: 'div',
13338             cn: [
13339                 {
13340                     tag: 'input',
13341                     type : 'hidden',
13342                     cls: 'form-hidden-field'
13343                 },
13344                 {
13345                     tag: 'ul',
13346                     cls: 'roo-select2-choices',
13347                     cn:[
13348                         {
13349                             tag: 'li',
13350                             cls: 'roo-select2-search-field',
13351                             cn: [
13352                                 buttons
13353                             ]
13354                         }
13355                     ]
13356                 }
13357             ]
13358         };
13359         
13360         var combobox = {
13361             cls: 'roo-select2-container input-group roo-select2-container-multi',
13362             cn: [
13363                 
13364                 box
13365 //                {
13366 //                    tag: 'ul',
13367 //                    cls: 'typeahead typeahead-long dropdown-menu',
13368 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13369 //                }
13370             ]
13371         };
13372         
13373         if(this.hasFeedback && !this.allowBlank){
13374             
13375             var feedback = {
13376                 tag: 'span',
13377                 cls: 'glyphicon form-control-feedback'
13378             };
13379
13380             combobox.cn.push(feedback);
13381         }
13382         
13383         var indicator = {
13384             tag : 'i',
13385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13386             tooltip : 'This field is required'
13387         };
13388         if (Roo.bootstrap.version == 4) {
13389             indicator = {
13390                 tag : 'i',
13391                 style : 'display:none'
13392             };
13393         }
13394         if (align ==='left' && this.fieldLabel.length) {
13395             
13396             cfg.cls += ' roo-form-group-label-left row';
13397             
13398             cfg.cn = [
13399                 indicator,
13400                 {
13401                     tag: 'label',
13402                     'for' :  id,
13403                     cls : 'control-label col-form-label',
13404                     html : this.fieldLabel
13405
13406                 },
13407                 {
13408                     cls : "", 
13409                     cn: [
13410                         combobox
13411                     ]
13412                 }
13413
13414             ];
13415             
13416             var labelCfg = cfg.cn[1];
13417             var contentCfg = cfg.cn[2];
13418             
13419
13420             if(this.indicatorpos == 'right'){
13421                 
13422                 cfg.cn = [
13423                     {
13424                         tag: 'label',
13425                         'for' :  id,
13426                         cls : 'control-label col-form-label',
13427                         cn : [
13428                             {
13429                                 tag : 'span',
13430                                 html : this.fieldLabel
13431                             },
13432                             indicator
13433                         ]
13434                     },
13435                     {
13436                         cls : "",
13437                         cn: [
13438                             combobox
13439                         ]
13440                     }
13441
13442                 ];
13443                 
13444                 
13445                 
13446                 labelCfg = cfg.cn[0];
13447                 contentCfg = cfg.cn[1];
13448             
13449             }
13450             
13451             if(this.labelWidth > 12){
13452                 labelCfg.style = "width: " + this.labelWidth + 'px';
13453             }
13454             
13455             if(this.labelWidth < 13 && this.labelmd == 0){
13456                 this.labelmd = this.labelWidth;
13457             }
13458             
13459             if(this.labellg > 0){
13460                 labelCfg.cls += ' col-lg-' + this.labellg;
13461                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13462             }
13463             
13464             if(this.labelmd > 0){
13465                 labelCfg.cls += ' col-md-' + this.labelmd;
13466                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13467             }
13468             
13469             if(this.labelsm > 0){
13470                 labelCfg.cls += ' col-sm-' + this.labelsm;
13471                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13472             }
13473             
13474             if(this.labelxs > 0){
13475                 labelCfg.cls += ' col-xs-' + this.labelxs;
13476                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13477             }
13478                 
13479                 
13480         } else if ( this.fieldLabel.length) {
13481 //                Roo.log(" label");
13482                  cfg.cn = [
13483                    indicator,
13484                     {
13485                         tag: 'label',
13486                         //cls : 'input-group-addon',
13487                         html : this.fieldLabel
13488                     },
13489                     combobox
13490                 ];
13491                 
13492                 if(this.indicatorpos == 'right'){
13493                     cfg.cn = [
13494                         {
13495                             tag: 'label',
13496                             //cls : 'input-group-addon',
13497                             html : this.fieldLabel
13498                         },
13499                         indicator,
13500                         combobox
13501                     ];
13502                     
13503                 }
13504
13505         } else {
13506             
13507 //                Roo.log(" no label && no align");
13508                 cfg = combobox
13509                      
13510                 
13511         }
13512          
13513         var settings=this;
13514         ['xs','sm','md','lg'].map(function(size){
13515             if (settings[size]) {
13516                 cfg.cls += ' col-' + size + '-' + settings[size];
13517             }
13518         });
13519         
13520         return cfg;
13521         
13522     },
13523     
13524     _initEventsCalled : false,
13525     
13526     // private
13527     initEvents: function()
13528     {   
13529         if (this._initEventsCalled) { // as we call render... prevent looping...
13530             return;
13531         }
13532         this._initEventsCalled = true;
13533         
13534         if (!this.store) {
13535             throw "can not find store for combo";
13536         }
13537         
13538         this.indicator = this.indicatorEl();
13539         
13540         this.store = Roo.factory(this.store, Roo.data);
13541         this.store.parent = this;
13542         
13543         // if we are building from html. then this element is so complex, that we can not really
13544         // use the rendered HTML.
13545         // so we have to trash and replace the previous code.
13546         if (Roo.XComponent.build_from_html) {
13547             // remove this element....
13548             var e = this.el.dom, k=0;
13549             while (e ) { e = e.previousSibling;  ++k;}
13550
13551             this.el.remove();
13552             
13553             this.el=false;
13554             this.rendered = false;
13555             
13556             this.render(this.parent().getChildContainer(true), k);
13557         }
13558         
13559         if(Roo.isIOS && this.useNativeIOS){
13560             this.initIOSView();
13561             return;
13562         }
13563         
13564         /*
13565          * Touch Devices
13566          */
13567         
13568         if(Roo.isTouch && this.mobileTouchView){
13569             this.initTouchView();
13570             return;
13571         }
13572         
13573         if(this.tickable){
13574             this.initTickableEvents();
13575             return;
13576         }
13577         
13578         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13579         
13580         if(this.hiddenName){
13581             
13582             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13583             
13584             this.hiddenField.dom.value =
13585                 this.hiddenValue !== undefined ? this.hiddenValue :
13586                 this.value !== undefined ? this.value : '';
13587
13588             // prevent input submission
13589             this.el.dom.removeAttribute('name');
13590             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13591              
13592              
13593         }
13594         //if(Roo.isGecko){
13595         //    this.el.dom.setAttribute('autocomplete', 'off');
13596         //}
13597         
13598         var cls = 'x-combo-list';
13599         
13600         //this.list = new Roo.Layer({
13601         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13602         //});
13603         
13604         var _this = this;
13605         
13606         (function(){
13607             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13608             _this.list.setWidth(lw);
13609         }).defer(100);
13610         
13611         this.list.on('mouseover', this.onViewOver, this);
13612         this.list.on('mousemove', this.onViewMove, this);
13613         this.list.on('scroll', this.onViewScroll, this);
13614         
13615         /*
13616         this.list.swallowEvent('mousewheel');
13617         this.assetHeight = 0;
13618
13619         if(this.title){
13620             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13621             this.assetHeight += this.header.getHeight();
13622         }
13623
13624         this.innerList = this.list.createChild({cls:cls+'-inner'});
13625         this.innerList.on('mouseover', this.onViewOver, this);
13626         this.innerList.on('mousemove', this.onViewMove, this);
13627         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13628         
13629         if(this.allowBlank && !this.pageSize && !this.disableClear){
13630             this.footer = this.list.createChild({cls:cls+'-ft'});
13631             this.pageTb = new Roo.Toolbar(this.footer);
13632            
13633         }
13634         if(this.pageSize){
13635             this.footer = this.list.createChild({cls:cls+'-ft'});
13636             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13637                     {pageSize: this.pageSize});
13638             
13639         }
13640         
13641         if (this.pageTb && this.allowBlank && !this.disableClear) {
13642             var _this = this;
13643             this.pageTb.add(new Roo.Toolbar.Fill(), {
13644                 cls: 'x-btn-icon x-btn-clear',
13645                 text: '&#160;',
13646                 handler: function()
13647                 {
13648                     _this.collapse();
13649                     _this.clearValue();
13650                     _this.onSelect(false, -1);
13651                 }
13652             });
13653         }
13654         if (this.footer) {
13655             this.assetHeight += this.footer.getHeight();
13656         }
13657         */
13658             
13659         if(!this.tpl){
13660             this.tpl = Roo.bootstrap.version == 4 ?
13661                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13662                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13663         }
13664
13665         this.view = new Roo.View(this.list, this.tpl, {
13666             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13667         });
13668         //this.view.wrapEl.setDisplayed(false);
13669         this.view.on('click', this.onViewClick, this);
13670         
13671         
13672         this.store.on('beforeload', this.onBeforeLoad, this);
13673         this.store.on('load', this.onLoad, this);
13674         this.store.on('loadexception', this.onLoadException, this);
13675         /*
13676         if(this.resizable){
13677             this.resizer = new Roo.Resizable(this.list,  {
13678                pinned:true, handles:'se'
13679             });
13680             this.resizer.on('resize', function(r, w, h){
13681                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13682                 this.listWidth = w;
13683                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13684                 this.restrictHeight();
13685             }, this);
13686             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13687         }
13688         */
13689         if(!this.editable){
13690             this.editable = true;
13691             this.setEditable(false);
13692         }
13693         
13694         /*
13695         
13696         if (typeof(this.events.add.listeners) != 'undefined') {
13697             
13698             this.addicon = this.wrap.createChild(
13699                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13700        
13701             this.addicon.on('click', function(e) {
13702                 this.fireEvent('add', this);
13703             }, this);
13704         }
13705         if (typeof(this.events.edit.listeners) != 'undefined') {
13706             
13707             this.editicon = this.wrap.createChild(
13708                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13709             if (this.addicon) {
13710                 this.editicon.setStyle('margin-left', '40px');
13711             }
13712             this.editicon.on('click', function(e) {
13713                 
13714                 // we fire even  if inothing is selected..
13715                 this.fireEvent('edit', this, this.lastData );
13716                 
13717             }, this);
13718         }
13719         */
13720         
13721         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13722             "up" : function(e){
13723                 this.inKeyMode = true;
13724                 this.selectPrev();
13725             },
13726
13727             "down" : function(e){
13728                 if(!this.isExpanded()){
13729                     this.onTriggerClick();
13730                 }else{
13731                     this.inKeyMode = true;
13732                     this.selectNext();
13733                 }
13734             },
13735
13736             "enter" : function(e){
13737 //                this.onViewClick();
13738                 //return true;
13739                 this.collapse();
13740                 
13741                 if(this.fireEvent("specialkey", this, e)){
13742                     this.onViewClick(false);
13743                 }
13744                 
13745                 return true;
13746             },
13747
13748             "esc" : function(e){
13749                 this.collapse();
13750             },
13751
13752             "tab" : function(e){
13753                 this.collapse();
13754                 
13755                 if(this.fireEvent("specialkey", this, e)){
13756                     this.onViewClick(false);
13757                 }
13758                 
13759                 return true;
13760             },
13761
13762             scope : this,
13763
13764             doRelay : function(foo, bar, hname){
13765                 if(hname == 'down' || this.scope.isExpanded()){
13766                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13767                 }
13768                 return true;
13769             },
13770
13771             forceKeyDown: true
13772         });
13773         
13774         
13775         this.queryDelay = Math.max(this.queryDelay || 10,
13776                 this.mode == 'local' ? 10 : 250);
13777         
13778         
13779         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13780         
13781         if(this.typeAhead){
13782             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13783         }
13784         if(this.editable !== false){
13785             this.inputEl().on("keyup", this.onKeyUp, this);
13786         }
13787         if(this.forceSelection){
13788             this.inputEl().on('blur', this.doForce, this);
13789         }
13790         
13791         if(this.multiple){
13792             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13793             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13794         }
13795     },
13796     
13797     initTickableEvents: function()
13798     {   
13799         this.createList();
13800         
13801         if(this.hiddenName){
13802             
13803             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13804             
13805             this.hiddenField.dom.value =
13806                 this.hiddenValue !== undefined ? this.hiddenValue :
13807                 this.value !== undefined ? this.value : '';
13808
13809             // prevent input submission
13810             this.el.dom.removeAttribute('name');
13811             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13812              
13813              
13814         }
13815         
13816 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13817         
13818         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13819         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13820         if(this.triggerList){
13821             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13822         }
13823          
13824         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13825         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13826         
13827         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13828         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13829         
13830         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13831         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13832         
13833         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13834         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13835         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13836         
13837         this.okBtn.hide();
13838         this.cancelBtn.hide();
13839         
13840         var _this = this;
13841         
13842         (function(){
13843             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13844             _this.list.setWidth(lw);
13845         }).defer(100);
13846         
13847         this.list.on('mouseover', this.onViewOver, this);
13848         this.list.on('mousemove', this.onViewMove, this);
13849         
13850         this.list.on('scroll', this.onViewScroll, this);
13851         
13852         if(!this.tpl){
13853             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13854                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13855         }
13856
13857         this.view = new Roo.View(this.list, this.tpl, {
13858             singleSelect:true,
13859             tickable:true,
13860             parent:this,
13861             store: this.store,
13862             selectedClass: this.selectedClass
13863         });
13864         
13865         //this.view.wrapEl.setDisplayed(false);
13866         this.view.on('click', this.onViewClick, this);
13867         
13868         
13869         
13870         this.store.on('beforeload', this.onBeforeLoad, this);
13871         this.store.on('load', this.onLoad, this);
13872         this.store.on('loadexception', this.onLoadException, this);
13873         
13874         if(this.editable){
13875             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13876                 "up" : function(e){
13877                     this.inKeyMode = true;
13878                     this.selectPrev();
13879                 },
13880
13881                 "down" : function(e){
13882                     this.inKeyMode = true;
13883                     this.selectNext();
13884                 },
13885
13886                 "enter" : function(e){
13887                     if(this.fireEvent("specialkey", this, e)){
13888                         this.onViewClick(false);
13889                     }
13890                     
13891                     return true;
13892                 },
13893
13894                 "esc" : function(e){
13895                     this.onTickableFooterButtonClick(e, false, false);
13896                 },
13897
13898                 "tab" : function(e){
13899                     this.fireEvent("specialkey", this, e);
13900                     
13901                     this.onTickableFooterButtonClick(e, false, false);
13902                     
13903                     return true;
13904                 },
13905
13906                 scope : this,
13907
13908                 doRelay : function(e, fn, key){
13909                     if(this.scope.isExpanded()){
13910                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13911                     }
13912                     return true;
13913                 },
13914
13915                 forceKeyDown: true
13916             });
13917         }
13918         
13919         this.queryDelay = Math.max(this.queryDelay || 10,
13920                 this.mode == 'local' ? 10 : 250);
13921         
13922         
13923         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13924         
13925         if(this.typeAhead){
13926             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13927         }
13928         
13929         if(this.editable !== false){
13930             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13931         }
13932         
13933         this.indicator = this.indicatorEl();
13934         
13935         if(this.indicator){
13936             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13937             this.indicator.hide();
13938         }
13939         
13940     },
13941
13942     onDestroy : function(){
13943         if(this.view){
13944             this.view.setStore(null);
13945             this.view.el.removeAllListeners();
13946             this.view.el.remove();
13947             this.view.purgeListeners();
13948         }
13949         if(this.list){
13950             this.list.dom.innerHTML  = '';
13951         }
13952         
13953         if(this.store){
13954             this.store.un('beforeload', this.onBeforeLoad, this);
13955             this.store.un('load', this.onLoad, this);
13956             this.store.un('loadexception', this.onLoadException, this);
13957         }
13958         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13959     },
13960
13961     // private
13962     fireKey : function(e){
13963         if(e.isNavKeyPress() && !this.list.isVisible()){
13964             this.fireEvent("specialkey", this, e);
13965         }
13966     },
13967
13968     // private
13969     onResize: function(w, h){
13970 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13971 //        
13972 //        if(typeof w != 'number'){
13973 //            // we do not handle it!?!?
13974 //            return;
13975 //        }
13976 //        var tw = this.trigger.getWidth();
13977 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13978 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13979 //        var x = w - tw;
13980 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13981 //            
13982 //        //this.trigger.setStyle('left', x+'px');
13983 //        
13984 //        if(this.list && this.listWidth === undefined){
13985 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13986 //            this.list.setWidth(lw);
13987 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13988 //        }
13989         
13990     
13991         
13992     },
13993
13994     /**
13995      * Allow or prevent the user from directly editing the field text.  If false is passed,
13996      * the user will only be able to select from the items defined in the dropdown list.  This method
13997      * is the runtime equivalent of setting the 'editable' config option at config time.
13998      * @param {Boolean} value True to allow the user to directly edit the field text
13999      */
14000     setEditable : function(value){
14001         if(value == this.editable){
14002             return;
14003         }
14004         this.editable = value;
14005         if(!value){
14006             this.inputEl().dom.setAttribute('readOnly', true);
14007             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14008             this.inputEl().addClass('x-combo-noedit');
14009         }else{
14010             this.inputEl().dom.setAttribute('readOnly', false);
14011             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14012             this.inputEl().removeClass('x-combo-noedit');
14013         }
14014     },
14015
14016     // private
14017     
14018     onBeforeLoad : function(combo,opts){
14019         if(!this.hasFocus){
14020             return;
14021         }
14022          if (!opts.add) {
14023             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14024          }
14025         this.restrictHeight();
14026         this.selectedIndex = -1;
14027     },
14028
14029     // private
14030     onLoad : function(){
14031         
14032         this.hasQuery = false;
14033         
14034         if(!this.hasFocus){
14035             return;
14036         }
14037         
14038         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14039             this.loading.hide();
14040         }
14041         
14042         if(this.store.getCount() > 0){
14043             
14044             this.expand();
14045             this.restrictHeight();
14046             if(this.lastQuery == this.allQuery){
14047                 if(this.editable && !this.tickable){
14048                     this.inputEl().dom.select();
14049                 }
14050                 
14051                 if(
14052                     !this.selectByValue(this.value, true) &&
14053                     this.autoFocus && 
14054                     (
14055                         !this.store.lastOptions ||
14056                         typeof(this.store.lastOptions.add) == 'undefined' || 
14057                         this.store.lastOptions.add != true
14058                     )
14059                 ){
14060                     this.select(0, true);
14061                 }
14062             }else{
14063                 if(this.autoFocus){
14064                     this.selectNext();
14065                 }
14066                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14067                     this.taTask.delay(this.typeAheadDelay);
14068                 }
14069             }
14070         }else{
14071             this.onEmptyResults();
14072         }
14073         
14074         //this.el.focus();
14075     },
14076     // private
14077     onLoadException : function()
14078     {
14079         this.hasQuery = false;
14080         
14081         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14082             this.loading.hide();
14083         }
14084         
14085         if(this.tickable && this.editable){
14086             return;
14087         }
14088         
14089         this.collapse();
14090         // only causes errors at present
14091         //Roo.log(this.store.reader.jsonData);
14092         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14093             // fixme
14094             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14095         //}
14096         
14097         
14098     },
14099     // private
14100     onTypeAhead : function(){
14101         if(this.store.getCount() > 0){
14102             var r = this.store.getAt(0);
14103             var newValue = r.data[this.displayField];
14104             var len = newValue.length;
14105             var selStart = this.getRawValue().length;
14106             
14107             if(selStart != len){
14108                 this.setRawValue(newValue);
14109                 this.selectText(selStart, newValue.length);
14110             }
14111         }
14112     },
14113
14114     // private
14115     onSelect : function(record, index){
14116         
14117         if(this.fireEvent('beforeselect', this, record, index) !== false){
14118         
14119             this.setFromData(index > -1 ? record.data : false);
14120             
14121             this.collapse();
14122             this.fireEvent('select', this, record, index);
14123         }
14124     },
14125
14126     /**
14127      * Returns the currently selected field value or empty string if no value is set.
14128      * @return {String} value The selected value
14129      */
14130     getValue : function()
14131     {
14132         if(Roo.isIOS && this.useNativeIOS){
14133             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14134         }
14135         
14136         if(this.multiple){
14137             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14138         }
14139         
14140         if(this.valueField){
14141             return typeof this.value != 'undefined' ? this.value : '';
14142         }else{
14143             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14144         }
14145     },
14146     
14147     getRawValue : function()
14148     {
14149         if(Roo.isIOS && this.useNativeIOS){
14150             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14151         }
14152         
14153         var v = this.inputEl().getValue();
14154         
14155         return v;
14156     },
14157
14158     /**
14159      * Clears any text/value currently set in the field
14160      */
14161     clearValue : function(){
14162         
14163         if(this.hiddenField){
14164             this.hiddenField.dom.value = '';
14165         }
14166         this.value = '';
14167         this.setRawValue('');
14168         this.lastSelectionText = '';
14169         this.lastData = false;
14170         
14171         var close = this.closeTriggerEl();
14172         
14173         if(close){
14174             close.hide();
14175         }
14176         
14177         this.validate();
14178         
14179     },
14180
14181     /**
14182      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14183      * will be displayed in the field.  If the value does not match the data value of an existing item,
14184      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14185      * Otherwise the field will be blank (although the value will still be set).
14186      * @param {String} value The value to match
14187      */
14188     setValue : function(v)
14189     {
14190         if(Roo.isIOS && this.useNativeIOS){
14191             this.setIOSValue(v);
14192             return;
14193         }
14194         
14195         if(this.multiple){
14196             this.syncValue();
14197             return;
14198         }
14199         
14200         var text = v;
14201         if(this.valueField){
14202             var r = this.findRecord(this.valueField, v);
14203             if(r){
14204                 text = r.data[this.displayField];
14205             }else if(this.valueNotFoundText !== undefined){
14206                 text = this.valueNotFoundText;
14207             }
14208         }
14209         this.lastSelectionText = text;
14210         if(this.hiddenField){
14211             this.hiddenField.dom.value = v;
14212         }
14213         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14214         this.value = v;
14215         
14216         var close = this.closeTriggerEl();
14217         
14218         if(close){
14219             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14220         }
14221         
14222         this.validate();
14223     },
14224     /**
14225      * @property {Object} the last set data for the element
14226      */
14227     
14228     lastData : false,
14229     /**
14230      * Sets the value of the field based on a object which is related to the record format for the store.
14231      * @param {Object} value the value to set as. or false on reset?
14232      */
14233     setFromData : function(o){
14234         
14235         if(this.multiple){
14236             this.addItem(o);
14237             return;
14238         }
14239             
14240         var dv = ''; // display value
14241         var vv = ''; // value value..
14242         this.lastData = o;
14243         if (this.displayField) {
14244             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14245         } else {
14246             // this is an error condition!!!
14247             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14248         }
14249         
14250         if(this.valueField){
14251             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14252         }
14253         
14254         var close = this.closeTriggerEl();
14255         
14256         if(close){
14257             if(dv.length || vv * 1 > 0){
14258                 close.show() ;
14259                 this.blockFocus=true;
14260             } else {
14261                 close.hide();
14262             }             
14263         }
14264         
14265         if(this.hiddenField){
14266             this.hiddenField.dom.value = vv;
14267             
14268             this.lastSelectionText = dv;
14269             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14270             this.value = vv;
14271             return;
14272         }
14273         // no hidden field.. - we store the value in 'value', but still display
14274         // display field!!!!
14275         this.lastSelectionText = dv;
14276         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14277         this.value = vv;
14278         
14279         
14280         
14281     },
14282     // private
14283     reset : function(){
14284         // overridden so that last data is reset..
14285         
14286         if(this.multiple){
14287             this.clearItem();
14288             return;
14289         }
14290         
14291         this.setValue(this.originalValue);
14292         //this.clearInvalid();
14293         this.lastData = false;
14294         if (this.view) {
14295             this.view.clearSelections();
14296         }
14297         
14298         this.validate();
14299     },
14300     // private
14301     findRecord : function(prop, value){
14302         var record;
14303         if(this.store.getCount() > 0){
14304             this.store.each(function(r){
14305                 if(r.data[prop] == value){
14306                     record = r;
14307                     return false;
14308                 }
14309                 return true;
14310             });
14311         }
14312         return record;
14313     },
14314     
14315     getName: function()
14316     {
14317         // returns hidden if it's set..
14318         if (!this.rendered) {return ''};
14319         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14320         
14321     },
14322     // private
14323     onViewMove : function(e, t){
14324         this.inKeyMode = false;
14325     },
14326
14327     // private
14328     onViewOver : function(e, t){
14329         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14330             return;
14331         }
14332         var item = this.view.findItemFromChild(t);
14333         
14334         if(item){
14335             var index = this.view.indexOf(item);
14336             this.select(index, false);
14337         }
14338     },
14339
14340     // private
14341     onViewClick : function(view, doFocus, el, e)
14342     {
14343         var index = this.view.getSelectedIndexes()[0];
14344         
14345         var r = this.store.getAt(index);
14346         
14347         if(this.tickable){
14348             
14349             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14350                 return;
14351             }
14352             
14353             var rm = false;
14354             var _this = this;
14355             
14356             Roo.each(this.tickItems, function(v,k){
14357                 
14358                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14359                     Roo.log(v);
14360                     _this.tickItems.splice(k, 1);
14361                     
14362                     if(typeof(e) == 'undefined' && view == false){
14363                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14364                     }
14365                     
14366                     rm = true;
14367                     return;
14368                 }
14369             });
14370             
14371             if(rm){
14372                 return;
14373             }
14374             
14375             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14376                 this.tickItems.push(r.data);
14377             }
14378             
14379             if(typeof(e) == 'undefined' && view == false){
14380                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14381             }
14382                     
14383             return;
14384         }
14385         
14386         if(r){
14387             this.onSelect(r, index);
14388         }
14389         if(doFocus !== false && !this.blockFocus){
14390             this.inputEl().focus();
14391         }
14392     },
14393
14394     // private
14395     restrictHeight : function(){
14396         //this.innerList.dom.style.height = '';
14397         //var inner = this.innerList.dom;
14398         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14399         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14400         //this.list.beginUpdate();
14401         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14402         this.list.alignTo(this.inputEl(), this.listAlign);
14403         this.list.alignTo(this.inputEl(), this.listAlign);
14404         //this.list.endUpdate();
14405     },
14406
14407     // private
14408     onEmptyResults : function(){
14409         
14410         if(this.tickable && this.editable){
14411             this.hasFocus = false;
14412             this.restrictHeight();
14413             return;
14414         }
14415         
14416         this.collapse();
14417     },
14418
14419     /**
14420      * Returns true if the dropdown list is expanded, else false.
14421      */
14422     isExpanded : function(){
14423         return this.list.isVisible();
14424     },
14425
14426     /**
14427      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14428      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14429      * @param {String} value The data value of the item to select
14430      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14431      * selected item if it is not currently in view (defaults to true)
14432      * @return {Boolean} True if the value matched an item in the list, else false
14433      */
14434     selectByValue : function(v, scrollIntoView){
14435         if(v !== undefined && v !== null){
14436             var r = this.findRecord(this.valueField || this.displayField, v);
14437             if(r){
14438                 this.select(this.store.indexOf(r), scrollIntoView);
14439                 return true;
14440             }
14441         }
14442         return false;
14443     },
14444
14445     /**
14446      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14447      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14448      * @param {Number} index The zero-based index of the list item to select
14449      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14450      * selected item if it is not currently in view (defaults to true)
14451      */
14452     select : function(index, scrollIntoView){
14453         this.selectedIndex = index;
14454         this.view.select(index);
14455         if(scrollIntoView !== false){
14456             var el = this.view.getNode(index);
14457             /*
14458              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14459              */
14460             if(el){
14461                 this.list.scrollChildIntoView(el, false);
14462             }
14463         }
14464     },
14465
14466     // private
14467     selectNext : function(){
14468         var ct = this.store.getCount();
14469         if(ct > 0){
14470             if(this.selectedIndex == -1){
14471                 this.select(0);
14472             }else if(this.selectedIndex < ct-1){
14473                 this.select(this.selectedIndex+1);
14474             }
14475         }
14476     },
14477
14478     // private
14479     selectPrev : function(){
14480         var ct = this.store.getCount();
14481         if(ct > 0){
14482             if(this.selectedIndex == -1){
14483                 this.select(0);
14484             }else if(this.selectedIndex != 0){
14485                 this.select(this.selectedIndex-1);
14486             }
14487         }
14488     },
14489
14490     // private
14491     onKeyUp : function(e){
14492         if(this.editable !== false && !e.isSpecialKey()){
14493             this.lastKey = e.getKey();
14494             this.dqTask.delay(this.queryDelay);
14495         }
14496     },
14497
14498     // private
14499     validateBlur : function(){
14500         return !this.list || !this.list.isVisible();   
14501     },
14502
14503     // private
14504     initQuery : function(){
14505         
14506         var v = this.getRawValue();
14507         
14508         if(this.tickable && this.editable){
14509             v = this.tickableInputEl().getValue();
14510         }
14511         
14512         this.doQuery(v);
14513     },
14514
14515     // private
14516     doForce : function(){
14517         if(this.inputEl().dom.value.length > 0){
14518             this.inputEl().dom.value =
14519                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14520              
14521         }
14522     },
14523
14524     /**
14525      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14526      * query allowing the query action to be canceled if needed.
14527      * @param {String} query The SQL query to execute
14528      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14529      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14530      * saved in the current store (defaults to false)
14531      */
14532     doQuery : function(q, forceAll){
14533         
14534         if(q === undefined || q === null){
14535             q = '';
14536         }
14537         var qe = {
14538             query: q,
14539             forceAll: forceAll,
14540             combo: this,
14541             cancel:false
14542         };
14543         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14544             return false;
14545         }
14546         q = qe.query;
14547         
14548         forceAll = qe.forceAll;
14549         if(forceAll === true || (q.length >= this.minChars)){
14550             
14551             this.hasQuery = true;
14552             
14553             if(this.lastQuery != q || this.alwaysQuery){
14554                 this.lastQuery = q;
14555                 if(this.mode == 'local'){
14556                     this.selectedIndex = -1;
14557                     if(forceAll){
14558                         this.store.clearFilter();
14559                     }else{
14560                         
14561                         if(this.specialFilter){
14562                             this.fireEvent('specialfilter', this);
14563                             this.onLoad();
14564                             return;
14565                         }
14566                         
14567                         this.store.filter(this.displayField, q);
14568                     }
14569                     
14570                     this.store.fireEvent("datachanged", this.store);
14571                     
14572                     this.onLoad();
14573                     
14574                     
14575                 }else{
14576                     
14577                     this.store.baseParams[this.queryParam] = q;
14578                     
14579                     var options = {params : this.getParams(q)};
14580                     
14581                     if(this.loadNext){
14582                         options.add = true;
14583                         options.params.start = this.page * this.pageSize;
14584                     }
14585                     
14586                     this.store.load(options);
14587                     
14588                     /*
14589                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14590                      *  we should expand the list on onLoad
14591                      *  so command out it
14592                      */
14593 //                    this.expand();
14594                 }
14595             }else{
14596                 this.selectedIndex = -1;
14597                 this.onLoad();   
14598             }
14599         }
14600         
14601         this.loadNext = false;
14602     },
14603     
14604     // private
14605     getParams : function(q){
14606         var p = {};
14607         //p[this.queryParam] = q;
14608         
14609         if(this.pageSize){
14610             p.start = 0;
14611             p.limit = this.pageSize;
14612         }
14613         return p;
14614     },
14615
14616     /**
14617      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14618      */
14619     collapse : function(){
14620         if(!this.isExpanded()){
14621             return;
14622         }
14623         
14624         this.list.hide();
14625         
14626         this.hasFocus = false;
14627         
14628         if(this.tickable){
14629             this.okBtn.hide();
14630             this.cancelBtn.hide();
14631             this.trigger.show();
14632             
14633             if(this.editable){
14634                 this.tickableInputEl().dom.value = '';
14635                 this.tickableInputEl().blur();
14636             }
14637             
14638         }
14639         
14640         Roo.get(document).un('mousedown', this.collapseIf, this);
14641         Roo.get(document).un('mousewheel', this.collapseIf, this);
14642         if (!this.editable) {
14643             Roo.get(document).un('keydown', this.listKeyPress, this);
14644         }
14645         this.fireEvent('collapse', this);
14646         
14647         this.validate();
14648     },
14649
14650     // private
14651     collapseIf : function(e){
14652         var in_combo  = e.within(this.el);
14653         var in_list =  e.within(this.list);
14654         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14655         
14656         if (in_combo || in_list || is_list) {
14657             //e.stopPropagation();
14658             return;
14659         }
14660         
14661         if(this.tickable){
14662             this.onTickableFooterButtonClick(e, false, false);
14663         }
14664
14665         this.collapse();
14666         
14667     },
14668
14669     /**
14670      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14671      */
14672     expand : function(){
14673        
14674         if(this.isExpanded() || !this.hasFocus){
14675             return;
14676         }
14677         
14678         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14679         this.list.setWidth(lw);
14680         
14681         Roo.log('expand');
14682         
14683         this.list.show();
14684         
14685         this.restrictHeight();
14686         
14687         if(this.tickable){
14688             
14689             this.tickItems = Roo.apply([], this.item);
14690             
14691             this.okBtn.show();
14692             this.cancelBtn.show();
14693             this.trigger.hide();
14694             
14695             if(this.editable){
14696                 this.tickableInputEl().focus();
14697             }
14698             
14699         }
14700         
14701         Roo.get(document).on('mousedown', this.collapseIf, this);
14702         Roo.get(document).on('mousewheel', this.collapseIf, this);
14703         if (!this.editable) {
14704             Roo.get(document).on('keydown', this.listKeyPress, this);
14705         }
14706         
14707         this.fireEvent('expand', this);
14708     },
14709
14710     // private
14711     // Implements the default empty TriggerField.onTriggerClick function
14712     onTriggerClick : function(e)
14713     {
14714         Roo.log('trigger click');
14715         
14716         if(this.disabled || !this.triggerList){
14717             return;
14718         }
14719         
14720         this.page = 0;
14721         this.loadNext = false;
14722         
14723         if(this.isExpanded()){
14724             this.collapse();
14725             if (!this.blockFocus) {
14726                 this.inputEl().focus();
14727             }
14728             
14729         }else {
14730             this.hasFocus = true;
14731             if(this.triggerAction == 'all') {
14732                 this.doQuery(this.allQuery, true);
14733             } else {
14734                 this.doQuery(this.getRawValue());
14735             }
14736             if (!this.blockFocus) {
14737                 this.inputEl().focus();
14738             }
14739         }
14740     },
14741     
14742     onTickableTriggerClick : function(e)
14743     {
14744         if(this.disabled){
14745             return;
14746         }
14747         
14748         this.page = 0;
14749         this.loadNext = false;
14750         this.hasFocus = true;
14751         
14752         if(this.triggerAction == 'all') {
14753             this.doQuery(this.allQuery, true);
14754         } else {
14755             this.doQuery(this.getRawValue());
14756         }
14757     },
14758     
14759     onSearchFieldClick : function(e)
14760     {
14761         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14762             this.onTickableFooterButtonClick(e, false, false);
14763             return;
14764         }
14765         
14766         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14767             return;
14768         }
14769         
14770         this.page = 0;
14771         this.loadNext = false;
14772         this.hasFocus = true;
14773         
14774         if(this.triggerAction == 'all') {
14775             this.doQuery(this.allQuery, true);
14776         } else {
14777             this.doQuery(this.getRawValue());
14778         }
14779     },
14780     
14781     listKeyPress : function(e)
14782     {
14783         //Roo.log('listkeypress');
14784         // scroll to first matching element based on key pres..
14785         if (e.isSpecialKey()) {
14786             return false;
14787         }
14788         var k = String.fromCharCode(e.getKey()).toUpperCase();
14789         //Roo.log(k);
14790         var match  = false;
14791         var csel = this.view.getSelectedNodes();
14792         var cselitem = false;
14793         if (csel.length) {
14794             var ix = this.view.indexOf(csel[0]);
14795             cselitem  = this.store.getAt(ix);
14796             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14797                 cselitem = false;
14798             }
14799             
14800         }
14801         
14802         this.store.each(function(v) { 
14803             if (cselitem) {
14804                 // start at existing selection.
14805                 if (cselitem.id == v.id) {
14806                     cselitem = false;
14807                 }
14808                 return true;
14809             }
14810                 
14811             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14812                 match = this.store.indexOf(v);
14813                 return false;
14814             }
14815             return true;
14816         }, this);
14817         
14818         if (match === false) {
14819             return true; // no more action?
14820         }
14821         // scroll to?
14822         this.view.select(match);
14823         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14824         sn.scrollIntoView(sn.dom.parentNode, false);
14825     },
14826     
14827     onViewScroll : function(e, t){
14828         
14829         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){
14830             return;
14831         }
14832         
14833         this.hasQuery = true;
14834         
14835         this.loading = this.list.select('.loading', true).first();
14836         
14837         if(this.loading === null){
14838             this.list.createChild({
14839                 tag: 'div',
14840                 cls: 'loading roo-select2-more-results roo-select2-active',
14841                 html: 'Loading more results...'
14842             });
14843             
14844             this.loading = this.list.select('.loading', true).first();
14845             
14846             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14847             
14848             this.loading.hide();
14849         }
14850         
14851         this.loading.show();
14852         
14853         var _combo = this;
14854         
14855         this.page++;
14856         this.loadNext = true;
14857         
14858         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14859         
14860         return;
14861     },
14862     
14863     addItem : function(o)
14864     {   
14865         var dv = ''; // display value
14866         
14867         if (this.displayField) {
14868             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14869         } else {
14870             // this is an error condition!!!
14871             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14872         }
14873         
14874         if(!dv.length){
14875             return;
14876         }
14877         
14878         var choice = this.choices.createChild({
14879             tag: 'li',
14880             cls: 'roo-select2-search-choice',
14881             cn: [
14882                 {
14883                     tag: 'div',
14884                     html: dv
14885                 },
14886                 {
14887                     tag: 'a',
14888                     href: '#',
14889                     cls: 'roo-select2-search-choice-close fa fa-times',
14890                     tabindex: '-1'
14891                 }
14892             ]
14893             
14894         }, this.searchField);
14895         
14896         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14897         
14898         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14899         
14900         this.item.push(o);
14901         
14902         this.lastData = o;
14903         
14904         this.syncValue();
14905         
14906         this.inputEl().dom.value = '';
14907         
14908         this.validate();
14909     },
14910     
14911     onRemoveItem : function(e, _self, o)
14912     {
14913         e.preventDefault();
14914         
14915         this.lastItem = Roo.apply([], this.item);
14916         
14917         var index = this.item.indexOf(o.data) * 1;
14918         
14919         if( index < 0){
14920             Roo.log('not this item?!');
14921             return;
14922         }
14923         
14924         this.item.splice(index, 1);
14925         o.item.remove();
14926         
14927         this.syncValue();
14928         
14929         this.fireEvent('remove', this, e);
14930         
14931         this.validate();
14932         
14933     },
14934     
14935     syncValue : function()
14936     {
14937         if(!this.item.length){
14938             this.clearValue();
14939             return;
14940         }
14941             
14942         var value = [];
14943         var _this = this;
14944         Roo.each(this.item, function(i){
14945             if(_this.valueField){
14946                 value.push(i[_this.valueField]);
14947                 return;
14948             }
14949
14950             value.push(i);
14951         });
14952
14953         this.value = value.join(',');
14954
14955         if(this.hiddenField){
14956             this.hiddenField.dom.value = this.value;
14957         }
14958         
14959         this.store.fireEvent("datachanged", this.store);
14960         
14961         this.validate();
14962     },
14963     
14964     clearItem : function()
14965     {
14966         if(!this.multiple){
14967             return;
14968         }
14969         
14970         this.item = [];
14971         
14972         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14973            c.remove();
14974         });
14975         
14976         this.syncValue();
14977         
14978         this.validate();
14979         
14980         if(this.tickable && !Roo.isTouch){
14981             this.view.refresh();
14982         }
14983     },
14984     
14985     inputEl: function ()
14986     {
14987         if(Roo.isIOS && this.useNativeIOS){
14988             return this.el.select('select.roo-ios-select', true).first();
14989         }
14990         
14991         if(Roo.isTouch && this.mobileTouchView){
14992             return this.el.select('input.form-control',true).first();
14993         }
14994         
14995         if(this.tickable){
14996             return this.searchField;
14997         }
14998         
14999         return this.el.select('input.form-control',true).first();
15000     },
15001     
15002     onTickableFooterButtonClick : function(e, btn, el)
15003     {
15004         e.preventDefault();
15005         
15006         this.lastItem = Roo.apply([], this.item);
15007         
15008         if(btn && btn.name == 'cancel'){
15009             this.tickItems = Roo.apply([], this.item);
15010             this.collapse();
15011             return;
15012         }
15013         
15014         this.clearItem();
15015         
15016         var _this = this;
15017         
15018         Roo.each(this.tickItems, function(o){
15019             _this.addItem(o);
15020         });
15021         
15022         this.collapse();
15023         
15024     },
15025     
15026     validate : function()
15027     {
15028         if(this.getVisibilityEl().hasClass('hidden')){
15029             return true;
15030         }
15031         
15032         var v = this.getRawValue();
15033         
15034         if(this.multiple){
15035             v = this.getValue();
15036         }
15037         
15038         if(this.disabled || this.allowBlank || v.length){
15039             this.markValid();
15040             return true;
15041         }
15042         
15043         this.markInvalid();
15044         return false;
15045     },
15046     
15047     tickableInputEl : function()
15048     {
15049         if(!this.tickable || !this.editable){
15050             return this.inputEl();
15051         }
15052         
15053         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15054     },
15055     
15056     
15057     getAutoCreateTouchView : function()
15058     {
15059         var id = Roo.id();
15060         
15061         var cfg = {
15062             cls: 'form-group' //input-group
15063         };
15064         
15065         var input =  {
15066             tag: 'input',
15067             id : id,
15068             type : this.inputType,
15069             cls : 'form-control x-combo-noedit',
15070             autocomplete: 'new-password',
15071             placeholder : this.placeholder || '',
15072             readonly : true
15073         };
15074         
15075         if (this.name) {
15076             input.name = this.name;
15077         }
15078         
15079         if (this.size) {
15080             input.cls += ' input-' + this.size;
15081         }
15082         
15083         if (this.disabled) {
15084             input.disabled = true;
15085         }
15086         
15087         var inputblock = {
15088             cls : '',
15089             cn : [
15090                 input
15091             ]
15092         };
15093         
15094         if(this.before){
15095             inputblock.cls += ' input-group';
15096             
15097             inputblock.cn.unshift({
15098                 tag :'span',
15099                 cls : 'input-group-addon input-group-prepend input-group-text',
15100                 html : this.before
15101             });
15102         }
15103         
15104         if(this.removable && !this.multiple){
15105             inputblock.cls += ' roo-removable';
15106             
15107             inputblock.cn.push({
15108                 tag: 'button',
15109                 html : 'x',
15110                 cls : 'roo-combo-removable-btn close'
15111             });
15112         }
15113
15114         if(this.hasFeedback && !this.allowBlank){
15115             
15116             inputblock.cls += ' has-feedback';
15117             
15118             inputblock.cn.push({
15119                 tag: 'span',
15120                 cls: 'glyphicon form-control-feedback'
15121             });
15122             
15123         }
15124         
15125         if (this.after) {
15126             
15127             inputblock.cls += (this.before) ? '' : ' input-group';
15128             
15129             inputblock.cn.push({
15130                 tag :'span',
15131                 cls : 'input-group-addon input-group-append input-group-text',
15132                 html : this.after
15133             });
15134         }
15135
15136         
15137         var ibwrap = inputblock;
15138         
15139         if(this.multiple){
15140             ibwrap = {
15141                 tag: 'ul',
15142                 cls: 'roo-select2-choices',
15143                 cn:[
15144                     {
15145                         tag: 'li',
15146                         cls: 'roo-select2-search-field',
15147                         cn: [
15148
15149                             inputblock
15150                         ]
15151                     }
15152                 ]
15153             };
15154         
15155             
15156         }
15157         
15158         var combobox = {
15159             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15160             cn: [
15161                 {
15162                     tag: 'input',
15163                     type : 'hidden',
15164                     cls: 'form-hidden-field'
15165                 },
15166                 ibwrap
15167             ]
15168         };
15169         
15170         if(!this.multiple && this.showToggleBtn){
15171             
15172             var caret = {
15173                         tag: 'span',
15174                         cls: 'caret'
15175             };
15176             
15177             if (this.caret != false) {
15178                 caret = {
15179                      tag: 'i',
15180                      cls: 'fa fa-' + this.caret
15181                 };
15182                 
15183             }
15184             
15185             combobox.cn.push({
15186                 tag :'span',
15187                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15188                 cn : [
15189                     caret,
15190                     {
15191                         tag: 'span',
15192                         cls: 'combobox-clear',
15193                         cn  : [
15194                             {
15195                                 tag : 'i',
15196                                 cls: 'icon-remove'
15197                             }
15198                         ]
15199                     }
15200                 ]
15201
15202             })
15203         }
15204         
15205         if(this.multiple){
15206             combobox.cls += ' roo-select2-container-multi';
15207         }
15208         
15209         var align = this.labelAlign || this.parentLabelAlign();
15210         
15211         if (align ==='left' && this.fieldLabel.length) {
15212
15213             cfg.cn = [
15214                 {
15215                    tag : 'i',
15216                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15217                    tooltip : 'This field is required'
15218                 },
15219                 {
15220                     tag: 'label',
15221                     cls : 'control-label col-form-label',
15222                     html : this.fieldLabel
15223
15224                 },
15225                 {
15226                     cls : '', 
15227                     cn: [
15228                         combobox
15229                     ]
15230                 }
15231             ];
15232             
15233             var labelCfg = cfg.cn[1];
15234             var contentCfg = cfg.cn[2];
15235             
15236
15237             if(this.indicatorpos == 'right'){
15238                 cfg.cn = [
15239                     {
15240                         tag: 'label',
15241                         'for' :  id,
15242                         cls : 'control-label col-form-label',
15243                         cn : [
15244                             {
15245                                 tag : 'span',
15246                                 html : this.fieldLabel
15247                             },
15248                             {
15249                                 tag : 'i',
15250                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15251                                 tooltip : 'This field is required'
15252                             }
15253                         ]
15254                     },
15255                     {
15256                         cls : "",
15257                         cn: [
15258                             combobox
15259                         ]
15260                     }
15261
15262                 ];
15263                 
15264                 labelCfg = cfg.cn[0];
15265                 contentCfg = cfg.cn[1];
15266             }
15267             
15268            
15269             
15270             if(this.labelWidth > 12){
15271                 labelCfg.style = "width: " + this.labelWidth + 'px';
15272             }
15273             
15274             if(this.labelWidth < 13 && this.labelmd == 0){
15275                 this.labelmd = this.labelWidth;
15276             }
15277             
15278             if(this.labellg > 0){
15279                 labelCfg.cls += ' col-lg-' + this.labellg;
15280                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15281             }
15282             
15283             if(this.labelmd > 0){
15284                 labelCfg.cls += ' col-md-' + this.labelmd;
15285                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15286             }
15287             
15288             if(this.labelsm > 0){
15289                 labelCfg.cls += ' col-sm-' + this.labelsm;
15290                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15291             }
15292             
15293             if(this.labelxs > 0){
15294                 labelCfg.cls += ' col-xs-' + this.labelxs;
15295                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15296             }
15297                 
15298                 
15299         } else if ( this.fieldLabel.length) {
15300             cfg.cn = [
15301                 {
15302                    tag : 'i',
15303                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15304                    tooltip : 'This field is required'
15305                 },
15306                 {
15307                     tag: 'label',
15308                     cls : 'control-label',
15309                     html : this.fieldLabel
15310
15311                 },
15312                 {
15313                     cls : '', 
15314                     cn: [
15315                         combobox
15316                     ]
15317                 }
15318             ];
15319             
15320             if(this.indicatorpos == 'right'){
15321                 cfg.cn = [
15322                     {
15323                         tag: 'label',
15324                         cls : 'control-label',
15325                         html : this.fieldLabel,
15326                         cn : [
15327                             {
15328                                tag : 'i',
15329                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15330                                tooltip : 'This field is required'
15331                             }
15332                         ]
15333                     },
15334                     {
15335                         cls : '', 
15336                         cn: [
15337                             combobox
15338                         ]
15339                     }
15340                 ];
15341             }
15342         } else {
15343             cfg.cn = combobox;    
15344         }
15345         
15346         
15347         var settings = this;
15348         
15349         ['xs','sm','md','lg'].map(function(size){
15350             if (settings[size]) {
15351                 cfg.cls += ' col-' + size + '-' + settings[size];
15352             }
15353         });
15354         
15355         return cfg;
15356     },
15357     
15358     initTouchView : function()
15359     {
15360         this.renderTouchView();
15361         
15362         this.touchViewEl.on('scroll', function(){
15363             this.el.dom.scrollTop = 0;
15364         }, this);
15365         
15366         this.originalValue = this.getValue();
15367         
15368         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15369         
15370         this.inputEl().on("click", this.showTouchView, this);
15371         if (this.triggerEl) {
15372             this.triggerEl.on("click", this.showTouchView, this);
15373         }
15374         
15375         
15376         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15377         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15378         
15379         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15380         
15381         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15382         this.store.on('load', this.onTouchViewLoad, this);
15383         this.store.on('loadexception', this.onTouchViewLoadException, this);
15384         
15385         if(this.hiddenName){
15386             
15387             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15388             
15389             this.hiddenField.dom.value =
15390                 this.hiddenValue !== undefined ? this.hiddenValue :
15391                 this.value !== undefined ? this.value : '';
15392         
15393             this.el.dom.removeAttribute('name');
15394             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15395         }
15396         
15397         if(this.multiple){
15398             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15399             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15400         }
15401         
15402         if(this.removable && !this.multiple){
15403             var close = this.closeTriggerEl();
15404             if(close){
15405                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15406                 close.on('click', this.removeBtnClick, this, close);
15407             }
15408         }
15409         /*
15410          * fix the bug in Safari iOS8
15411          */
15412         this.inputEl().on("focus", function(e){
15413             document.activeElement.blur();
15414         }, this);
15415         
15416         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15417         
15418         return;
15419         
15420         
15421     },
15422     
15423     renderTouchView : function()
15424     {
15425         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15426         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15427         
15428         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15429         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15430         
15431         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15432         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15433         this.touchViewBodyEl.setStyle('overflow', 'auto');
15434         
15435         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15436         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15437         
15438         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15439         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15440         
15441     },
15442     
15443     showTouchView : function()
15444     {
15445         if(this.disabled){
15446             return;
15447         }
15448         
15449         this.touchViewHeaderEl.hide();
15450
15451         if(this.modalTitle.length){
15452             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15453             this.touchViewHeaderEl.show();
15454         }
15455
15456         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15457         this.touchViewEl.show();
15458
15459         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15460         
15461         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15462         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15463
15464         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15465
15466         if(this.modalTitle.length){
15467             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15468         }
15469         
15470         this.touchViewBodyEl.setHeight(bodyHeight);
15471
15472         if(this.animate){
15473             var _this = this;
15474             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15475         }else{
15476             this.touchViewEl.addClass('in');
15477         }
15478         
15479         if(this._touchViewMask){
15480             Roo.get(document.body).addClass("x-body-masked");
15481             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15482             this._touchViewMask.setStyle('z-index', 10000);
15483             this._touchViewMask.addClass('show');
15484         }
15485         
15486         this.doTouchViewQuery();
15487         
15488     },
15489     
15490     hideTouchView : function()
15491     {
15492         this.touchViewEl.removeClass('in');
15493
15494         if(this.animate){
15495             var _this = this;
15496             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15497         }else{
15498             this.touchViewEl.setStyle('display', 'none');
15499         }
15500         
15501         if(this._touchViewMask){
15502             this._touchViewMask.removeClass('show');
15503             Roo.get(document.body).removeClass("x-body-masked");
15504         }
15505     },
15506     
15507     setTouchViewValue : function()
15508     {
15509         if(this.multiple){
15510             this.clearItem();
15511         
15512             var _this = this;
15513
15514             Roo.each(this.tickItems, function(o){
15515                 this.addItem(o);
15516             }, this);
15517         }
15518         
15519         this.hideTouchView();
15520     },
15521     
15522     doTouchViewQuery : function()
15523     {
15524         var qe = {
15525             query: '',
15526             forceAll: true,
15527             combo: this,
15528             cancel:false
15529         };
15530         
15531         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15532             return false;
15533         }
15534         
15535         if(!this.alwaysQuery || this.mode == 'local'){
15536             this.onTouchViewLoad();
15537             return;
15538         }
15539         
15540         this.store.load();
15541     },
15542     
15543     onTouchViewBeforeLoad : function(combo,opts)
15544     {
15545         return;
15546     },
15547
15548     // private
15549     onTouchViewLoad : function()
15550     {
15551         if(this.store.getCount() < 1){
15552             this.onTouchViewEmptyResults();
15553             return;
15554         }
15555         
15556         this.clearTouchView();
15557         
15558         var rawValue = this.getRawValue();
15559         
15560         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15561         
15562         this.tickItems = [];
15563         
15564         this.store.data.each(function(d, rowIndex){
15565             var row = this.touchViewListGroup.createChild(template);
15566             
15567             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15568                 row.addClass(d.data.cls);
15569             }
15570             
15571             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15572                 var cfg = {
15573                     data : d.data,
15574                     html : d.data[this.displayField]
15575                 };
15576                 
15577                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15578                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15579                 }
15580             }
15581             row.removeClass('selected');
15582             if(!this.multiple && this.valueField &&
15583                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15584             {
15585                 // radio buttons..
15586                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15587                 row.addClass('selected');
15588             }
15589             
15590             if(this.multiple && this.valueField &&
15591                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15592             {
15593                 
15594                 // checkboxes...
15595                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15596                 this.tickItems.push(d.data);
15597             }
15598             
15599             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15600             
15601         }, this);
15602         
15603         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15604         
15605         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15606
15607         if(this.modalTitle.length){
15608             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15609         }
15610
15611         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15612         
15613         if(this.mobile_restrict_height && listHeight < bodyHeight){
15614             this.touchViewBodyEl.setHeight(listHeight);
15615         }
15616         
15617         var _this = this;
15618         
15619         if(firstChecked && listHeight > bodyHeight){
15620             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15621         }
15622         
15623     },
15624     
15625     onTouchViewLoadException : function()
15626     {
15627         this.hideTouchView();
15628     },
15629     
15630     onTouchViewEmptyResults : function()
15631     {
15632         this.clearTouchView();
15633         
15634         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15635         
15636         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15637         
15638     },
15639     
15640     clearTouchView : function()
15641     {
15642         this.touchViewListGroup.dom.innerHTML = '';
15643     },
15644     
15645     onTouchViewClick : function(e, el, o)
15646     {
15647         e.preventDefault();
15648         
15649         var row = o.row;
15650         var rowIndex = o.rowIndex;
15651         
15652         var r = this.store.getAt(rowIndex);
15653         
15654         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15655             
15656             if(!this.multiple){
15657                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15658                     c.dom.removeAttribute('checked');
15659                 }, this);
15660
15661                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15662
15663                 this.setFromData(r.data);
15664
15665                 var close = this.closeTriggerEl();
15666
15667                 if(close){
15668                     close.show();
15669                 }
15670
15671                 this.hideTouchView();
15672
15673                 this.fireEvent('select', this, r, rowIndex);
15674
15675                 return;
15676             }
15677
15678             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15679                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15680                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15681                 return;
15682             }
15683
15684             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15685             this.addItem(r.data);
15686             this.tickItems.push(r.data);
15687         }
15688     },
15689     
15690     getAutoCreateNativeIOS : function()
15691     {
15692         var cfg = {
15693             cls: 'form-group' //input-group,
15694         };
15695         
15696         var combobox =  {
15697             tag: 'select',
15698             cls : 'roo-ios-select'
15699         };
15700         
15701         if (this.name) {
15702             combobox.name = this.name;
15703         }
15704         
15705         if (this.disabled) {
15706             combobox.disabled = true;
15707         }
15708         
15709         var settings = this;
15710         
15711         ['xs','sm','md','lg'].map(function(size){
15712             if (settings[size]) {
15713                 cfg.cls += ' col-' + size + '-' + settings[size];
15714             }
15715         });
15716         
15717         cfg.cn = combobox;
15718         
15719         return cfg;
15720         
15721     },
15722     
15723     initIOSView : function()
15724     {
15725         this.store.on('load', this.onIOSViewLoad, this);
15726         
15727         return;
15728     },
15729     
15730     onIOSViewLoad : function()
15731     {
15732         if(this.store.getCount() < 1){
15733             return;
15734         }
15735         
15736         this.clearIOSView();
15737         
15738         if(this.allowBlank) {
15739             
15740             var default_text = '-- SELECT --';
15741             
15742             if(this.placeholder.length){
15743                 default_text = this.placeholder;
15744             }
15745             
15746             if(this.emptyTitle.length){
15747                 default_text += ' - ' + this.emptyTitle + ' -';
15748             }
15749             
15750             var opt = this.inputEl().createChild({
15751                 tag: 'option',
15752                 value : 0,
15753                 html : default_text
15754             });
15755             
15756             var o = {};
15757             o[this.valueField] = 0;
15758             o[this.displayField] = default_text;
15759             
15760             this.ios_options.push({
15761                 data : o,
15762                 el : opt
15763             });
15764             
15765         }
15766         
15767         this.store.data.each(function(d, rowIndex){
15768             
15769             var html = '';
15770             
15771             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15772                 html = d.data[this.displayField];
15773             }
15774             
15775             var value = '';
15776             
15777             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15778                 value = d.data[this.valueField];
15779             }
15780             
15781             var option = {
15782                 tag: 'option',
15783                 value : value,
15784                 html : html
15785             };
15786             
15787             if(this.value == d.data[this.valueField]){
15788                 option['selected'] = true;
15789             }
15790             
15791             var opt = this.inputEl().createChild(option);
15792             
15793             this.ios_options.push({
15794                 data : d.data,
15795                 el : opt
15796             });
15797             
15798         }, this);
15799         
15800         this.inputEl().on('change', function(){
15801            this.fireEvent('select', this);
15802         }, this);
15803         
15804     },
15805     
15806     clearIOSView: function()
15807     {
15808         this.inputEl().dom.innerHTML = '';
15809         
15810         this.ios_options = [];
15811     },
15812     
15813     setIOSValue: function(v)
15814     {
15815         this.value = v;
15816         
15817         if(!this.ios_options){
15818             return;
15819         }
15820         
15821         Roo.each(this.ios_options, function(opts){
15822            
15823            opts.el.dom.removeAttribute('selected');
15824            
15825            if(opts.data[this.valueField] != v){
15826                return;
15827            }
15828            
15829            opts.el.dom.setAttribute('selected', true);
15830            
15831         }, this);
15832     }
15833
15834     /** 
15835     * @cfg {Boolean} grow 
15836     * @hide 
15837     */
15838     /** 
15839     * @cfg {Number} growMin 
15840     * @hide 
15841     */
15842     /** 
15843     * @cfg {Number} growMax 
15844     * @hide 
15845     */
15846     /**
15847      * @hide
15848      * @method autoSize
15849      */
15850 });
15851
15852 Roo.apply(Roo.bootstrap.ComboBox,  {
15853     
15854     header : {
15855         tag: 'div',
15856         cls: 'modal-header',
15857         cn: [
15858             {
15859                 tag: 'h4',
15860                 cls: 'modal-title'
15861             }
15862         ]
15863     },
15864     
15865     body : {
15866         tag: 'div',
15867         cls: 'modal-body',
15868         cn: [
15869             {
15870                 tag: 'ul',
15871                 cls: 'list-group'
15872             }
15873         ]
15874     },
15875     
15876     listItemRadio : {
15877         tag: 'li',
15878         cls: 'list-group-item',
15879         cn: [
15880             {
15881                 tag: 'span',
15882                 cls: 'roo-combobox-list-group-item-value'
15883             },
15884             {
15885                 tag: 'div',
15886                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15887                 cn: [
15888                     {
15889                         tag: 'input',
15890                         type: 'radio'
15891                     },
15892                     {
15893                         tag: 'label'
15894                     }
15895                 ]
15896             }
15897         ]
15898     },
15899     
15900     listItemCheckbox : {
15901         tag: 'li',
15902         cls: 'list-group-item',
15903         cn: [
15904             {
15905                 tag: 'span',
15906                 cls: 'roo-combobox-list-group-item-value'
15907             },
15908             {
15909                 tag: 'div',
15910                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15911                 cn: [
15912                     {
15913                         tag: 'input',
15914                         type: 'checkbox'
15915                     },
15916                     {
15917                         tag: 'label'
15918                     }
15919                 ]
15920             }
15921         ]
15922     },
15923     
15924     emptyResult : {
15925         tag: 'div',
15926         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15927     },
15928     
15929     footer : {
15930         tag: 'div',
15931         cls: 'modal-footer',
15932         cn: [
15933             {
15934                 tag: 'div',
15935                 cls: 'row',
15936                 cn: [
15937                     {
15938                         tag: 'div',
15939                         cls: 'col-xs-6 text-left',
15940                         cn: {
15941                             tag: 'button',
15942                             cls: 'btn btn-danger roo-touch-view-cancel',
15943                             html: 'Cancel'
15944                         }
15945                     },
15946                     {
15947                         tag: 'div',
15948                         cls: 'col-xs-6 text-right',
15949                         cn: {
15950                             tag: 'button',
15951                             cls: 'btn btn-success roo-touch-view-ok',
15952                             html: 'OK'
15953                         }
15954                     }
15955                 ]
15956             }
15957         ]
15958         
15959     }
15960 });
15961
15962 Roo.apply(Roo.bootstrap.ComboBox,  {
15963     
15964     touchViewTemplate : {
15965         tag: 'div',
15966         cls: 'modal fade roo-combobox-touch-view',
15967         cn: [
15968             {
15969                 tag: 'div',
15970                 cls: 'modal-dialog',
15971                 style : 'position:fixed', // we have to fix position....
15972                 cn: [
15973                     {
15974                         tag: 'div',
15975                         cls: 'modal-content',
15976                         cn: [
15977                             Roo.bootstrap.ComboBox.header,
15978                             Roo.bootstrap.ComboBox.body,
15979                             Roo.bootstrap.ComboBox.footer
15980                         ]
15981                     }
15982                 ]
15983             }
15984         ]
15985     }
15986 });/*
15987  * Based on:
15988  * Ext JS Library 1.1.1
15989  * Copyright(c) 2006-2007, Ext JS, LLC.
15990  *
15991  * Originally Released Under LGPL - original licence link has changed is not relivant.
15992  *
15993  * Fork - LGPL
15994  * <script type="text/javascript">
15995  */
15996
15997 /**
15998  * @class Roo.View
15999  * @extends Roo.util.Observable
16000  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16001  * This class also supports single and multi selection modes. <br>
16002  * Create a data model bound view:
16003  <pre><code>
16004  var store = new Roo.data.Store(...);
16005
16006  var view = new Roo.View({
16007     el : "my-element",
16008     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16009  
16010     singleSelect: true,
16011     selectedClass: "ydataview-selected",
16012     store: store
16013  });
16014
16015  // listen for node click?
16016  view.on("click", function(vw, index, node, e){
16017  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16018  });
16019
16020  // load XML data
16021  dataModel.load("foobar.xml");
16022  </code></pre>
16023  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16024  * <br><br>
16025  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16026  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16027  * 
16028  * Note: old style constructor is still suported (container, template, config)
16029  * 
16030  * @constructor
16031  * Create a new View
16032  * @param {Object} config The config object
16033  * 
16034  */
16035 Roo.View = function(config, depreciated_tpl, depreciated_config){
16036     
16037     this.parent = false;
16038     
16039     if (typeof(depreciated_tpl) == 'undefined') {
16040         // new way.. - universal constructor.
16041         Roo.apply(this, config);
16042         this.el  = Roo.get(this.el);
16043     } else {
16044         // old format..
16045         this.el  = Roo.get(config);
16046         this.tpl = depreciated_tpl;
16047         Roo.apply(this, depreciated_config);
16048     }
16049     this.wrapEl  = this.el.wrap().wrap();
16050     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16051     
16052     
16053     if(typeof(this.tpl) == "string"){
16054         this.tpl = new Roo.Template(this.tpl);
16055     } else {
16056         // support xtype ctors..
16057         this.tpl = new Roo.factory(this.tpl, Roo);
16058     }
16059     
16060     
16061     this.tpl.compile();
16062     
16063     /** @private */
16064     this.addEvents({
16065         /**
16066          * @event beforeclick
16067          * Fires before a click is processed. Returns false to cancel the default action.
16068          * @param {Roo.View} this
16069          * @param {Number} index The index of the target node
16070          * @param {HTMLElement} node The target node
16071          * @param {Roo.EventObject} e The raw event object
16072          */
16073             "beforeclick" : true,
16074         /**
16075          * @event click
16076          * Fires when a template node is clicked.
16077          * @param {Roo.View} this
16078          * @param {Number} index The index of the target node
16079          * @param {HTMLElement} node The target node
16080          * @param {Roo.EventObject} e The raw event object
16081          */
16082             "click" : true,
16083         /**
16084          * @event dblclick
16085          * Fires when a template node is double clicked.
16086          * @param {Roo.View} this
16087          * @param {Number} index The index of the target node
16088          * @param {HTMLElement} node The target node
16089          * @param {Roo.EventObject} e The raw event object
16090          */
16091             "dblclick" : true,
16092         /**
16093          * @event contextmenu
16094          * Fires when a template node is right clicked.
16095          * @param {Roo.View} this
16096          * @param {Number} index The index of the target node
16097          * @param {HTMLElement} node The target node
16098          * @param {Roo.EventObject} e The raw event object
16099          */
16100             "contextmenu" : true,
16101         /**
16102          * @event selectionchange
16103          * Fires when the selected nodes change.
16104          * @param {Roo.View} this
16105          * @param {Array} selections Array of the selected nodes
16106          */
16107             "selectionchange" : true,
16108     
16109         /**
16110          * @event beforeselect
16111          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16112          * @param {Roo.View} this
16113          * @param {HTMLElement} node The node to be selected
16114          * @param {Array} selections Array of currently selected nodes
16115          */
16116             "beforeselect" : true,
16117         /**
16118          * @event preparedata
16119          * Fires on every row to render, to allow you to change the data.
16120          * @param {Roo.View} this
16121          * @param {Object} data to be rendered (change this)
16122          */
16123           "preparedata" : true
16124           
16125           
16126         });
16127
16128
16129
16130     this.el.on({
16131         "click": this.onClick,
16132         "dblclick": this.onDblClick,
16133         "contextmenu": this.onContextMenu,
16134         scope:this
16135     });
16136
16137     this.selections = [];
16138     this.nodes = [];
16139     this.cmp = new Roo.CompositeElementLite([]);
16140     if(this.store){
16141         this.store = Roo.factory(this.store, Roo.data);
16142         this.setStore(this.store, true);
16143     }
16144     
16145     if ( this.footer && this.footer.xtype) {
16146            
16147          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16148         
16149         this.footer.dataSource = this.store;
16150         this.footer.container = fctr;
16151         this.footer = Roo.factory(this.footer, Roo);
16152         fctr.insertFirst(this.el);
16153         
16154         // this is a bit insane - as the paging toolbar seems to detach the el..
16155 //        dom.parentNode.parentNode.parentNode
16156          // they get detached?
16157     }
16158     
16159     
16160     Roo.View.superclass.constructor.call(this);
16161     
16162     
16163 };
16164
16165 Roo.extend(Roo.View, Roo.util.Observable, {
16166     
16167      /**
16168      * @cfg {Roo.data.Store} store Data store to load data from.
16169      */
16170     store : false,
16171     
16172     /**
16173      * @cfg {String|Roo.Element} el The container element.
16174      */
16175     el : '',
16176     
16177     /**
16178      * @cfg {String|Roo.Template} tpl The template used by this View 
16179      */
16180     tpl : false,
16181     /**
16182      * @cfg {String} dataName the named area of the template to use as the data area
16183      *                          Works with domtemplates roo-name="name"
16184      */
16185     dataName: false,
16186     /**
16187      * @cfg {String} selectedClass The css class to add to selected nodes
16188      */
16189     selectedClass : "x-view-selected",
16190      /**
16191      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16192      */
16193     emptyText : "",
16194     
16195     /**
16196      * @cfg {String} text to display on mask (default Loading)
16197      */
16198     mask : false,
16199     /**
16200      * @cfg {Boolean} multiSelect Allow multiple selection
16201      */
16202     multiSelect : false,
16203     /**
16204      * @cfg {Boolean} singleSelect Allow single selection
16205      */
16206     singleSelect:  false,
16207     
16208     /**
16209      * @cfg {Boolean} toggleSelect - selecting 
16210      */
16211     toggleSelect : false,
16212     
16213     /**
16214      * @cfg {Boolean} tickable - selecting 
16215      */
16216     tickable : false,
16217     
16218     /**
16219      * Returns the element this view is bound to.
16220      * @return {Roo.Element}
16221      */
16222     getEl : function(){
16223         return this.wrapEl;
16224     },
16225     
16226     
16227
16228     /**
16229      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16230      */
16231     refresh : function(){
16232         //Roo.log('refresh');
16233         var t = this.tpl;
16234         
16235         // if we are using something like 'domtemplate', then
16236         // the what gets used is:
16237         // t.applySubtemplate(NAME, data, wrapping data..)
16238         // the outer template then get' applied with
16239         //     the store 'extra data'
16240         // and the body get's added to the
16241         //      roo-name="data" node?
16242         //      <span class='roo-tpl-{name}'></span> ?????
16243         
16244         
16245         
16246         this.clearSelections();
16247         this.el.update("");
16248         var html = [];
16249         var records = this.store.getRange();
16250         if(records.length < 1) {
16251             
16252             // is this valid??  = should it render a template??
16253             
16254             this.el.update(this.emptyText);
16255             return;
16256         }
16257         var el = this.el;
16258         if (this.dataName) {
16259             this.el.update(t.apply(this.store.meta)); //????
16260             el = this.el.child('.roo-tpl-' + this.dataName);
16261         }
16262         
16263         for(var i = 0, len = records.length; i < len; i++){
16264             var data = this.prepareData(records[i].data, i, records[i]);
16265             this.fireEvent("preparedata", this, data, i, records[i]);
16266             
16267             var d = Roo.apply({}, data);
16268             
16269             if(this.tickable){
16270                 Roo.apply(d, {'roo-id' : Roo.id()});
16271                 
16272                 var _this = this;
16273             
16274                 Roo.each(this.parent.item, function(item){
16275                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16276                         return;
16277                     }
16278                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16279                 });
16280             }
16281             
16282             html[html.length] = Roo.util.Format.trim(
16283                 this.dataName ?
16284                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16285                     t.apply(d)
16286             );
16287         }
16288         
16289         
16290         
16291         el.update(html.join(""));
16292         this.nodes = el.dom.childNodes;
16293         this.updateIndexes(0);
16294     },
16295     
16296
16297     /**
16298      * Function to override to reformat the data that is sent to
16299      * the template for each node.
16300      * DEPRICATED - use the preparedata event handler.
16301      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16302      * a JSON object for an UpdateManager bound view).
16303      */
16304     prepareData : function(data, index, record)
16305     {
16306         this.fireEvent("preparedata", this, data, index, record);
16307         return data;
16308     },
16309
16310     onUpdate : function(ds, record){
16311         // Roo.log('on update');   
16312         this.clearSelections();
16313         var index = this.store.indexOf(record);
16314         var n = this.nodes[index];
16315         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16316         n.parentNode.removeChild(n);
16317         this.updateIndexes(index, index);
16318     },
16319
16320     
16321     
16322 // --------- FIXME     
16323     onAdd : function(ds, records, index)
16324     {
16325         //Roo.log(['on Add', ds, records, index] );        
16326         this.clearSelections();
16327         if(this.nodes.length == 0){
16328             this.refresh();
16329             return;
16330         }
16331         var n = this.nodes[index];
16332         for(var i = 0, len = records.length; i < len; i++){
16333             var d = this.prepareData(records[i].data, i, records[i]);
16334             if(n){
16335                 this.tpl.insertBefore(n, d);
16336             }else{
16337                 
16338                 this.tpl.append(this.el, d);
16339             }
16340         }
16341         this.updateIndexes(index);
16342     },
16343
16344     onRemove : function(ds, record, index){
16345        // Roo.log('onRemove');
16346         this.clearSelections();
16347         var el = this.dataName  ?
16348             this.el.child('.roo-tpl-' + this.dataName) :
16349             this.el; 
16350         
16351         el.dom.removeChild(this.nodes[index]);
16352         this.updateIndexes(index);
16353     },
16354
16355     /**
16356      * Refresh an individual node.
16357      * @param {Number} index
16358      */
16359     refreshNode : function(index){
16360         this.onUpdate(this.store, this.store.getAt(index));
16361     },
16362
16363     updateIndexes : function(startIndex, endIndex){
16364         var ns = this.nodes;
16365         startIndex = startIndex || 0;
16366         endIndex = endIndex || ns.length - 1;
16367         for(var i = startIndex; i <= endIndex; i++){
16368             ns[i].nodeIndex = i;
16369         }
16370     },
16371
16372     /**
16373      * Changes the data store this view uses and refresh the view.
16374      * @param {Store} store
16375      */
16376     setStore : function(store, initial){
16377         if(!initial && this.store){
16378             this.store.un("datachanged", this.refresh);
16379             this.store.un("add", this.onAdd);
16380             this.store.un("remove", this.onRemove);
16381             this.store.un("update", this.onUpdate);
16382             this.store.un("clear", this.refresh);
16383             this.store.un("beforeload", this.onBeforeLoad);
16384             this.store.un("load", this.onLoad);
16385             this.store.un("loadexception", this.onLoad);
16386         }
16387         if(store){
16388           
16389             store.on("datachanged", this.refresh, this);
16390             store.on("add", this.onAdd, this);
16391             store.on("remove", this.onRemove, this);
16392             store.on("update", this.onUpdate, this);
16393             store.on("clear", this.refresh, this);
16394             store.on("beforeload", this.onBeforeLoad, this);
16395             store.on("load", this.onLoad, this);
16396             store.on("loadexception", this.onLoad, this);
16397         }
16398         
16399         if(store){
16400             this.refresh();
16401         }
16402     },
16403     /**
16404      * onbeforeLoad - masks the loading area.
16405      *
16406      */
16407     onBeforeLoad : function(store,opts)
16408     {
16409          //Roo.log('onBeforeLoad');   
16410         if (!opts.add) {
16411             this.el.update("");
16412         }
16413         this.el.mask(this.mask ? this.mask : "Loading" ); 
16414     },
16415     onLoad : function ()
16416     {
16417         this.el.unmask();
16418     },
16419     
16420
16421     /**
16422      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16423      * @param {HTMLElement} node
16424      * @return {HTMLElement} The template node
16425      */
16426     findItemFromChild : function(node){
16427         var el = this.dataName  ?
16428             this.el.child('.roo-tpl-' + this.dataName,true) :
16429             this.el.dom; 
16430         
16431         if(!node || node.parentNode == el){
16432                     return node;
16433             }
16434             var p = node.parentNode;
16435             while(p && p != el){
16436             if(p.parentNode == el){
16437                 return p;
16438             }
16439             p = p.parentNode;
16440         }
16441             return null;
16442     },
16443
16444     /** @ignore */
16445     onClick : function(e){
16446         var item = this.findItemFromChild(e.getTarget());
16447         if(item){
16448             var index = this.indexOf(item);
16449             if(this.onItemClick(item, index, e) !== false){
16450                 this.fireEvent("click", this, index, item, e);
16451             }
16452         }else{
16453             this.clearSelections();
16454         }
16455     },
16456
16457     /** @ignore */
16458     onContextMenu : function(e){
16459         var item = this.findItemFromChild(e.getTarget());
16460         if(item){
16461             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16462         }
16463     },
16464
16465     /** @ignore */
16466     onDblClick : function(e){
16467         var item = this.findItemFromChild(e.getTarget());
16468         if(item){
16469             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16470         }
16471     },
16472
16473     onItemClick : function(item, index, e)
16474     {
16475         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16476             return false;
16477         }
16478         if (this.toggleSelect) {
16479             var m = this.isSelected(item) ? 'unselect' : 'select';
16480             //Roo.log(m);
16481             var _t = this;
16482             _t[m](item, true, false);
16483             return true;
16484         }
16485         if(this.multiSelect || this.singleSelect){
16486             if(this.multiSelect && e.shiftKey && this.lastSelection){
16487                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16488             }else{
16489                 this.select(item, this.multiSelect && e.ctrlKey);
16490                 this.lastSelection = item;
16491             }
16492             
16493             if(!this.tickable){
16494                 e.preventDefault();
16495             }
16496             
16497         }
16498         return true;
16499     },
16500
16501     /**
16502      * Get the number of selected nodes.
16503      * @return {Number}
16504      */
16505     getSelectionCount : function(){
16506         return this.selections.length;
16507     },
16508
16509     /**
16510      * Get the currently selected nodes.
16511      * @return {Array} An array of HTMLElements
16512      */
16513     getSelectedNodes : function(){
16514         return this.selections;
16515     },
16516
16517     /**
16518      * Get the indexes of the selected nodes.
16519      * @return {Array}
16520      */
16521     getSelectedIndexes : function(){
16522         var indexes = [], s = this.selections;
16523         for(var i = 0, len = s.length; i < len; i++){
16524             indexes.push(s[i].nodeIndex);
16525         }
16526         return indexes;
16527     },
16528
16529     /**
16530      * Clear all selections
16531      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16532      */
16533     clearSelections : function(suppressEvent){
16534         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16535             this.cmp.elements = this.selections;
16536             this.cmp.removeClass(this.selectedClass);
16537             this.selections = [];
16538             if(!suppressEvent){
16539                 this.fireEvent("selectionchange", this, this.selections);
16540             }
16541         }
16542     },
16543
16544     /**
16545      * Returns true if the passed node is selected
16546      * @param {HTMLElement/Number} node The node or node index
16547      * @return {Boolean}
16548      */
16549     isSelected : function(node){
16550         var s = this.selections;
16551         if(s.length < 1){
16552             return false;
16553         }
16554         node = this.getNode(node);
16555         return s.indexOf(node) !== -1;
16556     },
16557
16558     /**
16559      * Selects nodes.
16560      * @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
16561      * @param {Boolean} keepExisting (optional) true to keep existing selections
16562      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16563      */
16564     select : function(nodeInfo, keepExisting, suppressEvent){
16565         if(nodeInfo instanceof Array){
16566             if(!keepExisting){
16567                 this.clearSelections(true);
16568             }
16569             for(var i = 0, len = nodeInfo.length; i < len; i++){
16570                 this.select(nodeInfo[i], true, true);
16571             }
16572             return;
16573         } 
16574         var node = this.getNode(nodeInfo);
16575         if(!node || this.isSelected(node)){
16576             return; // already selected.
16577         }
16578         if(!keepExisting){
16579             this.clearSelections(true);
16580         }
16581         
16582         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16583             Roo.fly(node).addClass(this.selectedClass);
16584             this.selections.push(node);
16585             if(!suppressEvent){
16586                 this.fireEvent("selectionchange", this, this.selections);
16587             }
16588         }
16589         
16590         
16591     },
16592       /**
16593      * Unselects nodes.
16594      * @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
16595      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16596      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16597      */
16598     unselect : function(nodeInfo, keepExisting, suppressEvent)
16599     {
16600         if(nodeInfo instanceof Array){
16601             Roo.each(this.selections, function(s) {
16602                 this.unselect(s, nodeInfo);
16603             }, this);
16604             return;
16605         }
16606         var node = this.getNode(nodeInfo);
16607         if(!node || !this.isSelected(node)){
16608             //Roo.log("not selected");
16609             return; // not selected.
16610         }
16611         // fireevent???
16612         var ns = [];
16613         Roo.each(this.selections, function(s) {
16614             if (s == node ) {
16615                 Roo.fly(node).removeClass(this.selectedClass);
16616
16617                 return;
16618             }
16619             ns.push(s);
16620         },this);
16621         
16622         this.selections= ns;
16623         this.fireEvent("selectionchange", this, this.selections);
16624     },
16625
16626     /**
16627      * Gets a template node.
16628      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16629      * @return {HTMLElement} The node or null if it wasn't found
16630      */
16631     getNode : function(nodeInfo){
16632         if(typeof nodeInfo == "string"){
16633             return document.getElementById(nodeInfo);
16634         }else if(typeof nodeInfo == "number"){
16635             return this.nodes[nodeInfo];
16636         }
16637         return nodeInfo;
16638     },
16639
16640     /**
16641      * Gets a range template nodes.
16642      * @param {Number} startIndex
16643      * @param {Number} endIndex
16644      * @return {Array} An array of nodes
16645      */
16646     getNodes : function(start, end){
16647         var ns = this.nodes;
16648         start = start || 0;
16649         end = typeof end == "undefined" ? ns.length - 1 : end;
16650         var nodes = [];
16651         if(start <= end){
16652             for(var i = start; i <= end; i++){
16653                 nodes.push(ns[i]);
16654             }
16655         } else{
16656             for(var i = start; i >= end; i--){
16657                 nodes.push(ns[i]);
16658             }
16659         }
16660         return nodes;
16661     },
16662
16663     /**
16664      * Finds the index of the passed node
16665      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16666      * @return {Number} The index of the node or -1
16667      */
16668     indexOf : function(node){
16669         node = this.getNode(node);
16670         if(typeof node.nodeIndex == "number"){
16671             return node.nodeIndex;
16672         }
16673         var ns = this.nodes;
16674         for(var i = 0, len = ns.length; i < len; i++){
16675             if(ns[i] == node){
16676                 return i;
16677             }
16678         }
16679         return -1;
16680     }
16681 });
16682 /*
16683  * - LGPL
16684  *
16685  * based on jquery fullcalendar
16686  * 
16687  */
16688
16689 Roo.bootstrap = Roo.bootstrap || {};
16690 /**
16691  * @class Roo.bootstrap.Calendar
16692  * @extends Roo.bootstrap.Component
16693  * Bootstrap Calendar class
16694  * @cfg {Boolean} loadMask (true|false) default false
16695  * @cfg {Object} header generate the user specific header of the calendar, default false
16696
16697  * @constructor
16698  * Create a new Container
16699  * @param {Object} config The config object
16700  */
16701
16702
16703
16704 Roo.bootstrap.Calendar = function(config){
16705     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16706      this.addEvents({
16707         /**
16708              * @event select
16709              * Fires when a date is selected
16710              * @param {DatePicker} this
16711              * @param {Date} date The selected date
16712              */
16713         'select': true,
16714         /**
16715              * @event monthchange
16716              * Fires when the displayed month changes 
16717              * @param {DatePicker} this
16718              * @param {Date} date The selected month
16719              */
16720         'monthchange': true,
16721         /**
16722              * @event evententer
16723              * Fires when mouse over an event
16724              * @param {Calendar} this
16725              * @param {event} Event
16726              */
16727         'evententer': true,
16728         /**
16729              * @event eventleave
16730              * Fires when the mouse leaves an
16731              * @param {Calendar} this
16732              * @param {event}
16733              */
16734         'eventleave': true,
16735         /**
16736              * @event eventclick
16737              * Fires when the mouse click an
16738              * @param {Calendar} this
16739              * @param {event}
16740              */
16741         'eventclick': true
16742         
16743     });
16744
16745 };
16746
16747 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16748     
16749      /**
16750      * @cfg {Number} startDay
16751      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16752      */
16753     startDay : 0,
16754     
16755     loadMask : false,
16756     
16757     header : false,
16758       
16759     getAutoCreate : function(){
16760         
16761         
16762         var fc_button = function(name, corner, style, content ) {
16763             return Roo.apply({},{
16764                 tag : 'span',
16765                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16766                          (corner.length ?
16767                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16768                             ''
16769                         ),
16770                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16771                 unselectable: 'on'
16772             });
16773         };
16774         
16775         var header = {};
16776         
16777         if(!this.header){
16778             header = {
16779                 tag : 'table',
16780                 cls : 'fc-header',
16781                 style : 'width:100%',
16782                 cn : [
16783                     {
16784                         tag: 'tr',
16785                         cn : [
16786                             {
16787                                 tag : 'td',
16788                                 cls : 'fc-header-left',
16789                                 cn : [
16790                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16791                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16792                                     { tag: 'span', cls: 'fc-header-space' },
16793                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16794
16795
16796                                 ]
16797                             },
16798
16799                             {
16800                                 tag : 'td',
16801                                 cls : 'fc-header-center',
16802                                 cn : [
16803                                     {
16804                                         tag: 'span',
16805                                         cls: 'fc-header-title',
16806                                         cn : {
16807                                             tag: 'H2',
16808                                             html : 'month / year'
16809                                         }
16810                                     }
16811
16812                                 ]
16813                             },
16814                             {
16815                                 tag : 'td',
16816                                 cls : 'fc-header-right',
16817                                 cn : [
16818                               /*      fc_button('month', 'left', '', 'month' ),
16819                                     fc_button('week', '', '', 'week' ),
16820                                     fc_button('day', 'right', '', 'day' )
16821                                 */    
16822
16823                                 ]
16824                             }
16825
16826                         ]
16827                     }
16828                 ]
16829             };
16830         }
16831         
16832         header = this.header;
16833         
16834        
16835         var cal_heads = function() {
16836             var ret = [];
16837             // fixme - handle this.
16838             
16839             for (var i =0; i < Date.dayNames.length; i++) {
16840                 var d = Date.dayNames[i];
16841                 ret.push({
16842                     tag: 'th',
16843                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16844                     html : d.substring(0,3)
16845                 });
16846                 
16847             }
16848             ret[0].cls += ' fc-first';
16849             ret[6].cls += ' fc-last';
16850             return ret;
16851         };
16852         var cal_cell = function(n) {
16853             return  {
16854                 tag: 'td',
16855                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16856                 cn : [
16857                     {
16858                         cn : [
16859                             {
16860                                 cls: 'fc-day-number',
16861                                 html: 'D'
16862                             },
16863                             {
16864                                 cls: 'fc-day-content',
16865                              
16866                                 cn : [
16867                                      {
16868                                         style: 'position: relative;' // height: 17px;
16869                                     }
16870                                 ]
16871                             }
16872                             
16873                             
16874                         ]
16875                     }
16876                 ]
16877                 
16878             }
16879         };
16880         var cal_rows = function() {
16881             
16882             var ret = [];
16883             for (var r = 0; r < 6; r++) {
16884                 var row= {
16885                     tag : 'tr',
16886                     cls : 'fc-week',
16887                     cn : []
16888                 };
16889                 
16890                 for (var i =0; i < Date.dayNames.length; i++) {
16891                     var d = Date.dayNames[i];
16892                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16893
16894                 }
16895                 row.cn[0].cls+=' fc-first';
16896                 row.cn[0].cn[0].style = 'min-height:90px';
16897                 row.cn[6].cls+=' fc-last';
16898                 ret.push(row);
16899                 
16900             }
16901             ret[0].cls += ' fc-first';
16902             ret[4].cls += ' fc-prev-last';
16903             ret[5].cls += ' fc-last';
16904             return ret;
16905             
16906         };
16907         
16908         var cal_table = {
16909             tag: 'table',
16910             cls: 'fc-border-separate',
16911             style : 'width:100%',
16912             cellspacing  : 0,
16913             cn : [
16914                 { 
16915                     tag: 'thead',
16916                     cn : [
16917                         { 
16918                             tag: 'tr',
16919                             cls : 'fc-first fc-last',
16920                             cn : cal_heads()
16921                         }
16922                     ]
16923                 },
16924                 { 
16925                     tag: 'tbody',
16926                     cn : cal_rows()
16927                 }
16928                   
16929             ]
16930         };
16931          
16932          var cfg = {
16933             cls : 'fc fc-ltr',
16934             cn : [
16935                 header,
16936                 {
16937                     cls : 'fc-content',
16938                     style : "position: relative;",
16939                     cn : [
16940                         {
16941                             cls : 'fc-view fc-view-month fc-grid',
16942                             style : 'position: relative',
16943                             unselectable : 'on',
16944                             cn : [
16945                                 {
16946                                     cls : 'fc-event-container',
16947                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16948                                 },
16949                                 cal_table
16950                             ]
16951                         }
16952                     ]
16953     
16954                 }
16955            ] 
16956             
16957         };
16958         
16959          
16960         
16961         return cfg;
16962     },
16963     
16964     
16965     initEvents : function()
16966     {
16967         if(!this.store){
16968             throw "can not find store for calendar";
16969         }
16970         
16971         var mark = {
16972             tag: "div",
16973             cls:"x-dlg-mask",
16974             style: "text-align:center",
16975             cn: [
16976                 {
16977                     tag: "div",
16978                     style: "background-color:white;width:50%;margin:250 auto",
16979                     cn: [
16980                         {
16981                             tag: "img",
16982                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16983                         },
16984                         {
16985                             tag: "span",
16986                             html: "Loading"
16987                         }
16988                         
16989                     ]
16990                 }
16991             ]
16992         };
16993         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16994         
16995         var size = this.el.select('.fc-content', true).first().getSize();
16996         this.maskEl.setSize(size.width, size.height);
16997         this.maskEl.enableDisplayMode("block");
16998         if(!this.loadMask){
16999             this.maskEl.hide();
17000         }
17001         
17002         this.store = Roo.factory(this.store, Roo.data);
17003         this.store.on('load', this.onLoad, this);
17004         this.store.on('beforeload', this.onBeforeLoad, this);
17005         
17006         this.resize();
17007         
17008         this.cells = this.el.select('.fc-day',true);
17009         //Roo.log(this.cells);
17010         this.textNodes = this.el.query('.fc-day-number');
17011         this.cells.addClassOnOver('fc-state-hover');
17012         
17013         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17014         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17015         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17016         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17017         
17018         this.on('monthchange', this.onMonthChange, this);
17019         
17020         this.update(new Date().clearTime());
17021     },
17022     
17023     resize : function() {
17024         var sz  = this.el.getSize();
17025         
17026         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17027         this.el.select('.fc-day-content div',true).setHeight(34);
17028     },
17029     
17030     
17031     // private
17032     showPrevMonth : function(e){
17033         this.update(this.activeDate.add("mo", -1));
17034     },
17035     showToday : function(e){
17036         this.update(new Date().clearTime());
17037     },
17038     // private
17039     showNextMonth : function(e){
17040         this.update(this.activeDate.add("mo", 1));
17041     },
17042
17043     // private
17044     showPrevYear : function(){
17045         this.update(this.activeDate.add("y", -1));
17046     },
17047
17048     // private
17049     showNextYear : function(){
17050         this.update(this.activeDate.add("y", 1));
17051     },
17052
17053     
17054    // private
17055     update : function(date)
17056     {
17057         var vd = this.activeDate;
17058         this.activeDate = date;
17059 //        if(vd && this.el){
17060 //            var t = date.getTime();
17061 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17062 //                Roo.log('using add remove');
17063 //                
17064 //                this.fireEvent('monthchange', this, date);
17065 //                
17066 //                this.cells.removeClass("fc-state-highlight");
17067 //                this.cells.each(function(c){
17068 //                   if(c.dateValue == t){
17069 //                       c.addClass("fc-state-highlight");
17070 //                       setTimeout(function(){
17071 //                            try{c.dom.firstChild.focus();}catch(e){}
17072 //                       }, 50);
17073 //                       return false;
17074 //                   }
17075 //                   return true;
17076 //                });
17077 //                return;
17078 //            }
17079 //        }
17080         
17081         var days = date.getDaysInMonth();
17082         
17083         var firstOfMonth = date.getFirstDateOfMonth();
17084         var startingPos = firstOfMonth.getDay()-this.startDay;
17085         
17086         if(startingPos < this.startDay){
17087             startingPos += 7;
17088         }
17089         
17090         var pm = date.add(Date.MONTH, -1);
17091         var prevStart = pm.getDaysInMonth()-startingPos;
17092 //        
17093         this.cells = this.el.select('.fc-day',true);
17094         this.textNodes = this.el.query('.fc-day-number');
17095         this.cells.addClassOnOver('fc-state-hover');
17096         
17097         var cells = this.cells.elements;
17098         var textEls = this.textNodes;
17099         
17100         Roo.each(cells, function(cell){
17101             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17102         });
17103         
17104         days += startingPos;
17105
17106         // convert everything to numbers so it's fast
17107         var day = 86400000;
17108         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17109         //Roo.log(d);
17110         //Roo.log(pm);
17111         //Roo.log(prevStart);
17112         
17113         var today = new Date().clearTime().getTime();
17114         var sel = date.clearTime().getTime();
17115         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17116         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17117         var ddMatch = this.disabledDatesRE;
17118         var ddText = this.disabledDatesText;
17119         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17120         var ddaysText = this.disabledDaysText;
17121         var format = this.format;
17122         
17123         var setCellClass = function(cal, cell){
17124             cell.row = 0;
17125             cell.events = [];
17126             cell.more = [];
17127             //Roo.log('set Cell Class');
17128             cell.title = "";
17129             var t = d.getTime();
17130             
17131             //Roo.log(d);
17132             
17133             cell.dateValue = t;
17134             if(t == today){
17135                 cell.className += " fc-today";
17136                 cell.className += " fc-state-highlight";
17137                 cell.title = cal.todayText;
17138             }
17139             if(t == sel){
17140                 // disable highlight in other month..
17141                 //cell.className += " fc-state-highlight";
17142                 
17143             }
17144             // disabling
17145             if(t < min) {
17146                 cell.className = " fc-state-disabled";
17147                 cell.title = cal.minText;
17148                 return;
17149             }
17150             if(t > max) {
17151                 cell.className = " fc-state-disabled";
17152                 cell.title = cal.maxText;
17153                 return;
17154             }
17155             if(ddays){
17156                 if(ddays.indexOf(d.getDay()) != -1){
17157                     cell.title = ddaysText;
17158                     cell.className = " fc-state-disabled";
17159                 }
17160             }
17161             if(ddMatch && format){
17162                 var fvalue = d.dateFormat(format);
17163                 if(ddMatch.test(fvalue)){
17164                     cell.title = ddText.replace("%0", fvalue);
17165                     cell.className = " fc-state-disabled";
17166                 }
17167             }
17168             
17169             if (!cell.initialClassName) {
17170                 cell.initialClassName = cell.dom.className;
17171             }
17172             
17173             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17174         };
17175
17176         var i = 0;
17177         
17178         for(; i < startingPos; i++) {
17179             textEls[i].innerHTML = (++prevStart);
17180             d.setDate(d.getDate()+1);
17181             
17182             cells[i].className = "fc-past fc-other-month";
17183             setCellClass(this, cells[i]);
17184         }
17185         
17186         var intDay = 0;
17187         
17188         for(; i < days; i++){
17189             intDay = i - startingPos + 1;
17190             textEls[i].innerHTML = (intDay);
17191             d.setDate(d.getDate()+1);
17192             
17193             cells[i].className = ''; // "x-date-active";
17194             setCellClass(this, cells[i]);
17195         }
17196         var extraDays = 0;
17197         
17198         for(; i < 42; i++) {
17199             textEls[i].innerHTML = (++extraDays);
17200             d.setDate(d.getDate()+1);
17201             
17202             cells[i].className = "fc-future fc-other-month";
17203             setCellClass(this, cells[i]);
17204         }
17205         
17206         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17207         
17208         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17209         
17210         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17211         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17212         
17213         if(totalRows != 6){
17214             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17215             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17216         }
17217         
17218         this.fireEvent('monthchange', this, date);
17219         
17220         
17221         /*
17222         if(!this.internalRender){
17223             var main = this.el.dom.firstChild;
17224             var w = main.offsetWidth;
17225             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17226             Roo.fly(main).setWidth(w);
17227             this.internalRender = true;
17228             // opera does not respect the auto grow header center column
17229             // then, after it gets a width opera refuses to recalculate
17230             // without a second pass
17231             if(Roo.isOpera && !this.secondPass){
17232                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17233                 this.secondPass = true;
17234                 this.update.defer(10, this, [date]);
17235             }
17236         }
17237         */
17238         
17239     },
17240     
17241     findCell : function(dt) {
17242         dt = dt.clearTime().getTime();
17243         var ret = false;
17244         this.cells.each(function(c){
17245             //Roo.log("check " +c.dateValue + '?=' + dt);
17246             if(c.dateValue == dt){
17247                 ret = c;
17248                 return false;
17249             }
17250             return true;
17251         });
17252         
17253         return ret;
17254     },
17255     
17256     findCells : function(ev) {
17257         var s = ev.start.clone().clearTime().getTime();
17258        // Roo.log(s);
17259         var e= ev.end.clone().clearTime().getTime();
17260        // Roo.log(e);
17261         var ret = [];
17262         this.cells.each(function(c){
17263              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17264             
17265             if(c.dateValue > e){
17266                 return ;
17267             }
17268             if(c.dateValue < s){
17269                 return ;
17270             }
17271             ret.push(c);
17272         });
17273         
17274         return ret;    
17275     },
17276     
17277 //    findBestRow: function(cells)
17278 //    {
17279 //        var ret = 0;
17280 //        
17281 //        for (var i =0 ; i < cells.length;i++) {
17282 //            ret  = Math.max(cells[i].rows || 0,ret);
17283 //        }
17284 //        return ret;
17285 //        
17286 //    },
17287     
17288     
17289     addItem : function(ev)
17290     {
17291         // look for vertical location slot in
17292         var cells = this.findCells(ev);
17293         
17294 //        ev.row = this.findBestRow(cells);
17295         
17296         // work out the location.
17297         
17298         var crow = false;
17299         var rows = [];
17300         for(var i =0; i < cells.length; i++) {
17301             
17302             cells[i].row = cells[0].row;
17303             
17304             if(i == 0){
17305                 cells[i].row = cells[i].row + 1;
17306             }
17307             
17308             if (!crow) {
17309                 crow = {
17310                     start : cells[i],
17311                     end :  cells[i]
17312                 };
17313                 continue;
17314             }
17315             if (crow.start.getY() == cells[i].getY()) {
17316                 // on same row.
17317                 crow.end = cells[i];
17318                 continue;
17319             }
17320             // different row.
17321             rows.push(crow);
17322             crow = {
17323                 start: cells[i],
17324                 end : cells[i]
17325             };
17326             
17327         }
17328         
17329         rows.push(crow);
17330         ev.els = [];
17331         ev.rows = rows;
17332         ev.cells = cells;
17333         
17334         cells[0].events.push(ev);
17335         
17336         this.calevents.push(ev);
17337     },
17338     
17339     clearEvents: function() {
17340         
17341         if(!this.calevents){
17342             return;
17343         }
17344         
17345         Roo.each(this.cells.elements, function(c){
17346             c.row = 0;
17347             c.events = [];
17348             c.more = [];
17349         });
17350         
17351         Roo.each(this.calevents, function(e) {
17352             Roo.each(e.els, function(el) {
17353                 el.un('mouseenter' ,this.onEventEnter, this);
17354                 el.un('mouseleave' ,this.onEventLeave, this);
17355                 el.remove();
17356             },this);
17357         },this);
17358         
17359         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17360             e.remove();
17361         });
17362         
17363     },
17364     
17365     renderEvents: function()
17366     {   
17367         var _this = this;
17368         
17369         this.cells.each(function(c) {
17370             
17371             if(c.row < 5){
17372                 return;
17373             }
17374             
17375             var ev = c.events;
17376             
17377             var r = 4;
17378             if(c.row != c.events.length){
17379                 r = 4 - (4 - (c.row - c.events.length));
17380             }
17381             
17382             c.events = ev.slice(0, r);
17383             c.more = ev.slice(r);
17384             
17385             if(c.more.length && c.more.length == 1){
17386                 c.events.push(c.more.pop());
17387             }
17388             
17389             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17390             
17391         });
17392             
17393         this.cells.each(function(c) {
17394             
17395             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17396             
17397             
17398             for (var e = 0; e < c.events.length; e++){
17399                 var ev = c.events[e];
17400                 var rows = ev.rows;
17401                 
17402                 for(var i = 0; i < rows.length; i++) {
17403                 
17404                     // how many rows should it span..
17405
17406                     var  cfg = {
17407                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17408                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17409
17410                         unselectable : "on",
17411                         cn : [
17412                             {
17413                                 cls: 'fc-event-inner',
17414                                 cn : [
17415     //                                {
17416     //                                  tag:'span',
17417     //                                  cls: 'fc-event-time',
17418     //                                  html : cells.length > 1 ? '' : ev.time
17419     //                                },
17420                                     {
17421                                       tag:'span',
17422                                       cls: 'fc-event-title',
17423                                       html : String.format('{0}', ev.title)
17424                                     }
17425
17426
17427                                 ]
17428                             },
17429                             {
17430                                 cls: 'ui-resizable-handle ui-resizable-e',
17431                                 html : '&nbsp;&nbsp;&nbsp'
17432                             }
17433
17434                         ]
17435                     };
17436
17437                     if (i == 0) {
17438                         cfg.cls += ' fc-event-start';
17439                     }
17440                     if ((i+1) == rows.length) {
17441                         cfg.cls += ' fc-event-end';
17442                     }
17443
17444                     var ctr = _this.el.select('.fc-event-container',true).first();
17445                     var cg = ctr.createChild(cfg);
17446
17447                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17448                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17449
17450                     var r = (c.more.length) ? 1 : 0;
17451                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17452                     cg.setWidth(ebox.right - sbox.x -2);
17453
17454                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17455                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17456                     cg.on('click', _this.onEventClick, _this, ev);
17457
17458                     ev.els.push(cg);
17459                     
17460                 }
17461                 
17462             }
17463             
17464             
17465             if(c.more.length){
17466                 var  cfg = {
17467                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17468                     style : 'position: absolute',
17469                     unselectable : "on",
17470                     cn : [
17471                         {
17472                             cls: 'fc-event-inner',
17473                             cn : [
17474                                 {
17475                                   tag:'span',
17476                                   cls: 'fc-event-title',
17477                                   html : 'More'
17478                                 }
17479
17480
17481                             ]
17482                         },
17483                         {
17484                             cls: 'ui-resizable-handle ui-resizable-e',
17485                             html : '&nbsp;&nbsp;&nbsp'
17486                         }
17487
17488                     ]
17489                 };
17490
17491                 var ctr = _this.el.select('.fc-event-container',true).first();
17492                 var cg = ctr.createChild(cfg);
17493
17494                 var sbox = c.select('.fc-day-content',true).first().getBox();
17495                 var ebox = c.select('.fc-day-content',true).first().getBox();
17496                 //Roo.log(cg);
17497                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17498                 cg.setWidth(ebox.right - sbox.x -2);
17499
17500                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17501                 
17502             }
17503             
17504         });
17505         
17506         
17507         
17508     },
17509     
17510     onEventEnter: function (e, el,event,d) {
17511         this.fireEvent('evententer', this, el, event);
17512     },
17513     
17514     onEventLeave: function (e, el,event,d) {
17515         this.fireEvent('eventleave', this, el, event);
17516     },
17517     
17518     onEventClick: function (e, el,event,d) {
17519         this.fireEvent('eventclick', this, el, event);
17520     },
17521     
17522     onMonthChange: function () {
17523         this.store.load();
17524     },
17525     
17526     onMoreEventClick: function(e, el, more)
17527     {
17528         var _this = this;
17529         
17530         this.calpopover.placement = 'right';
17531         this.calpopover.setTitle('More');
17532         
17533         this.calpopover.setContent('');
17534         
17535         var ctr = this.calpopover.el.select('.popover-content', true).first();
17536         
17537         Roo.each(more, function(m){
17538             var cfg = {
17539                 cls : 'fc-event-hori fc-event-draggable',
17540                 html : m.title
17541             };
17542             var cg = ctr.createChild(cfg);
17543             
17544             cg.on('click', _this.onEventClick, _this, m);
17545         });
17546         
17547         this.calpopover.show(el);
17548         
17549         
17550     },
17551     
17552     onLoad: function () 
17553     {   
17554         this.calevents = [];
17555         var cal = this;
17556         
17557         if(this.store.getCount() > 0){
17558             this.store.data.each(function(d){
17559                cal.addItem({
17560                     id : d.data.id,
17561                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17562                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17563                     time : d.data.start_time,
17564                     title : d.data.title,
17565                     description : d.data.description,
17566                     venue : d.data.venue
17567                 });
17568             });
17569         }
17570         
17571         this.renderEvents();
17572         
17573         if(this.calevents.length && this.loadMask){
17574             this.maskEl.hide();
17575         }
17576     },
17577     
17578     onBeforeLoad: function()
17579     {
17580         this.clearEvents();
17581         if(this.loadMask){
17582             this.maskEl.show();
17583         }
17584     }
17585 });
17586
17587  
17588  /*
17589  * - LGPL
17590  *
17591  * element
17592  * 
17593  */
17594
17595 /**
17596  * @class Roo.bootstrap.Popover
17597  * @extends Roo.bootstrap.Component
17598  * Bootstrap Popover class
17599  * @cfg {String} html contents of the popover   (or false to use children..)
17600  * @cfg {String} title of popover (or false to hide)
17601  * @cfg {String} placement how it is placed
17602  * @cfg {String} trigger click || hover (or false to trigger manually)
17603  * @cfg {String} over what (parent or false to trigger manually.)
17604  * @cfg {Number} delay - delay before showing
17605  
17606  * @constructor
17607  * Create a new Popover
17608  * @param {Object} config The config object
17609  */
17610
17611 Roo.bootstrap.Popover = function(config){
17612     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17613     
17614     this.addEvents({
17615         // raw events
17616          /**
17617          * @event show
17618          * After the popover show
17619          * 
17620          * @param {Roo.bootstrap.Popover} this
17621          */
17622         "show" : true,
17623         /**
17624          * @event hide
17625          * After the popover hide
17626          * 
17627          * @param {Roo.bootstrap.Popover} this
17628          */
17629         "hide" : true
17630     });
17631 };
17632
17633 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17634     
17635     title: 'Fill in a title',
17636     html: false,
17637     
17638     placement : 'right',
17639     trigger : 'hover', // hover
17640     
17641     delay : 0,
17642     
17643     over: 'parent',
17644     
17645     can_build_overlaid : false,
17646     
17647     getChildContainer : function()
17648     {
17649         return this.el.select('.popover-content',true).first();
17650     },
17651     
17652     getAutoCreate : function(){
17653          
17654         var cfg = {
17655            cls : 'popover roo-dynamic',
17656            style: 'display:block',
17657            cn : [
17658                 {
17659                     cls : 'arrow'
17660                 },
17661                 {
17662                     cls : 'popover-inner',
17663                     cn : [
17664                         {
17665                             tag: 'h3',
17666                             cls: 'popover-title popover-header',
17667                             html : this.title
17668                         },
17669                         {
17670                             cls : 'popover-content popover-body',
17671                             html : this.html
17672                         }
17673                     ]
17674                     
17675                 }
17676            ]
17677         };
17678         
17679         return cfg;
17680     },
17681     setTitle: function(str)
17682     {
17683         this.title = str;
17684         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17685     },
17686     setContent: function(str)
17687     {
17688         this.html = str;
17689         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17690     },
17691     // as it get's added to the bottom of the page.
17692     onRender : function(ct, position)
17693     {
17694         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17695         if(!this.el){
17696             var cfg = Roo.apply({},  this.getAutoCreate());
17697             cfg.id = Roo.id();
17698             
17699             if (this.cls) {
17700                 cfg.cls += ' ' + this.cls;
17701             }
17702             if (this.style) {
17703                 cfg.style = this.style;
17704             }
17705             //Roo.log("adding to ");
17706             this.el = Roo.get(document.body).createChild(cfg, position);
17707 //            Roo.log(this.el);
17708         }
17709         this.initEvents();
17710     },
17711     
17712     initEvents : function()
17713     {
17714         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17715         this.el.enableDisplayMode('block');
17716         this.el.hide();
17717         if (this.over === false) {
17718             return; 
17719         }
17720         if (this.triggers === false) {
17721             return;
17722         }
17723         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17724         var triggers = this.trigger ? this.trigger.split(' ') : [];
17725         Roo.each(triggers, function(trigger) {
17726         
17727             if (trigger == 'click') {
17728                 on_el.on('click', this.toggle, this);
17729             } else if (trigger != 'manual') {
17730                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17731                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17732       
17733                 on_el.on(eventIn  ,this.enter, this);
17734                 on_el.on(eventOut, this.leave, this);
17735             }
17736         }, this);
17737         
17738     },
17739     
17740     
17741     // private
17742     timeout : null,
17743     hoverState : null,
17744     
17745     toggle : function () {
17746         this.hoverState == 'in' ? this.leave() : this.enter();
17747     },
17748     
17749     enter : function () {
17750         
17751         clearTimeout(this.timeout);
17752     
17753         this.hoverState = 'in';
17754     
17755         if (!this.delay || !this.delay.show) {
17756             this.show();
17757             return;
17758         }
17759         var _t = this;
17760         this.timeout = setTimeout(function () {
17761             if (_t.hoverState == 'in') {
17762                 _t.show();
17763             }
17764         }, this.delay.show)
17765     },
17766     
17767     leave : function() {
17768         clearTimeout(this.timeout);
17769     
17770         this.hoverState = 'out';
17771     
17772         if (!this.delay || !this.delay.hide) {
17773             this.hide();
17774             return;
17775         }
17776         var _t = this;
17777         this.timeout = setTimeout(function () {
17778             if (_t.hoverState == 'out') {
17779                 _t.hide();
17780             }
17781         }, this.delay.hide)
17782     },
17783     
17784     show : function (on_el)
17785     {
17786         if (!on_el) {
17787             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17788         }
17789         
17790         // set content.
17791         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17792         if (this.html !== false) {
17793             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17794         }
17795         this.el.removeClass([
17796             'fade','top','bottom', 'left', 'right','in',
17797             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17798         ]);
17799         if (!this.title.length) {
17800             this.el.select('.popover-title',true).hide();
17801         }
17802         
17803         var placement = typeof this.placement == 'function' ?
17804             this.placement.call(this, this.el, on_el) :
17805             this.placement;
17806             
17807         var autoToken = /\s?auto?\s?/i;
17808         var autoPlace = autoToken.test(placement);
17809         if (autoPlace) {
17810             placement = placement.replace(autoToken, '') || 'top';
17811         }
17812         
17813         //this.el.detach()
17814         //this.el.setXY([0,0]);
17815         this.el.show();
17816         this.el.dom.style.display='block';
17817         this.el.addClass(placement);
17818         
17819         //this.el.appendTo(on_el);
17820         
17821         var p = this.getPosition();
17822         var box = this.el.getBox();
17823         
17824         if (autoPlace) {
17825             // fixme..
17826         }
17827         var align = Roo.bootstrap.Popover.alignment[placement];
17828         
17829 //        Roo.log(align);
17830         this.el.alignTo(on_el, align[0],align[1]);
17831         //var arrow = this.el.select('.arrow',true).first();
17832         //arrow.set(align[2], 
17833         
17834         this.el.addClass('in');
17835         
17836         
17837         if (this.el.hasClass('fade')) {
17838             // fade it?
17839         }
17840         
17841         this.hoverState = 'in';
17842         
17843         this.fireEvent('show', this);
17844         
17845     },
17846     hide : function()
17847     {
17848         this.el.setXY([0,0]);
17849         this.el.removeClass('in');
17850         this.el.hide();
17851         this.hoverState = null;
17852         
17853         this.fireEvent('hide', this);
17854     }
17855     
17856 });
17857
17858 Roo.bootstrap.Popover.alignment = {
17859     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17860     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17861     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17862     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17863 };
17864
17865  /*
17866  * - LGPL
17867  *
17868  * Progress
17869  * 
17870  */
17871
17872 /**
17873  * @class Roo.bootstrap.Progress
17874  * @extends Roo.bootstrap.Component
17875  * Bootstrap Progress class
17876  * @cfg {Boolean} striped striped of the progress bar
17877  * @cfg {Boolean} active animated of the progress bar
17878  * 
17879  * 
17880  * @constructor
17881  * Create a new Progress
17882  * @param {Object} config The config object
17883  */
17884
17885 Roo.bootstrap.Progress = function(config){
17886     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17887 };
17888
17889 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17890     
17891     striped : false,
17892     active: false,
17893     
17894     getAutoCreate : function(){
17895         var cfg = {
17896             tag: 'div',
17897             cls: 'progress'
17898         };
17899         
17900         
17901         if(this.striped){
17902             cfg.cls += ' progress-striped';
17903         }
17904       
17905         if(this.active){
17906             cfg.cls += ' active';
17907         }
17908         
17909         
17910         return cfg;
17911     }
17912    
17913 });
17914
17915  
17916
17917  /*
17918  * - LGPL
17919  *
17920  * ProgressBar
17921  * 
17922  */
17923
17924 /**
17925  * @class Roo.bootstrap.ProgressBar
17926  * @extends Roo.bootstrap.Component
17927  * Bootstrap ProgressBar class
17928  * @cfg {Number} aria_valuenow aria-value now
17929  * @cfg {Number} aria_valuemin aria-value min
17930  * @cfg {Number} aria_valuemax aria-value max
17931  * @cfg {String} label label for the progress bar
17932  * @cfg {String} panel (success | info | warning | danger )
17933  * @cfg {String} role role of the progress bar
17934  * @cfg {String} sr_only text
17935  * 
17936  * 
17937  * @constructor
17938  * Create a new ProgressBar
17939  * @param {Object} config The config object
17940  */
17941
17942 Roo.bootstrap.ProgressBar = function(config){
17943     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17944 };
17945
17946 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17947     
17948     aria_valuenow : 0,
17949     aria_valuemin : 0,
17950     aria_valuemax : 100,
17951     label : false,
17952     panel : false,
17953     role : false,
17954     sr_only: false,
17955     
17956     getAutoCreate : function()
17957     {
17958         
17959         var cfg = {
17960             tag: 'div',
17961             cls: 'progress-bar',
17962             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17963         };
17964         
17965         if(this.sr_only){
17966             cfg.cn = {
17967                 tag: 'span',
17968                 cls: 'sr-only',
17969                 html: this.sr_only
17970             }
17971         }
17972         
17973         if(this.role){
17974             cfg.role = this.role;
17975         }
17976         
17977         if(this.aria_valuenow){
17978             cfg['aria-valuenow'] = this.aria_valuenow;
17979         }
17980         
17981         if(this.aria_valuemin){
17982             cfg['aria-valuemin'] = this.aria_valuemin;
17983         }
17984         
17985         if(this.aria_valuemax){
17986             cfg['aria-valuemax'] = this.aria_valuemax;
17987         }
17988         
17989         if(this.label && !this.sr_only){
17990             cfg.html = this.label;
17991         }
17992         
17993         if(this.panel){
17994             cfg.cls += ' progress-bar-' + this.panel;
17995         }
17996         
17997         return cfg;
17998     },
17999     
18000     update : function(aria_valuenow)
18001     {
18002         this.aria_valuenow = aria_valuenow;
18003         
18004         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18005     }
18006    
18007 });
18008
18009  
18010
18011  /*
18012  * - LGPL
18013  *
18014  * column
18015  * 
18016  */
18017
18018 /**
18019  * @class Roo.bootstrap.TabGroup
18020  * @extends Roo.bootstrap.Column
18021  * Bootstrap Column class
18022  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18023  * @cfg {Boolean} carousel true to make the group behave like a carousel
18024  * @cfg {Boolean} bullets show bullets for the panels
18025  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18026  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18027  * @cfg {Boolean} showarrow (true|false) show arrow default true
18028  * 
18029  * @constructor
18030  * Create a new TabGroup
18031  * @param {Object} config The config object
18032  */
18033
18034 Roo.bootstrap.TabGroup = function(config){
18035     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18036     if (!this.navId) {
18037         this.navId = Roo.id();
18038     }
18039     this.tabs = [];
18040     Roo.bootstrap.TabGroup.register(this);
18041     
18042 };
18043
18044 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18045     
18046     carousel : false,
18047     transition : false,
18048     bullets : 0,
18049     timer : 0,
18050     autoslide : false,
18051     slideFn : false,
18052     slideOnTouch : false,
18053     showarrow : true,
18054     
18055     getAutoCreate : function()
18056     {
18057         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18058         
18059         cfg.cls += ' tab-content';
18060         
18061         if (this.carousel) {
18062             cfg.cls += ' carousel slide';
18063             
18064             cfg.cn = [{
18065                cls : 'carousel-inner',
18066                cn : []
18067             }];
18068         
18069             if(this.bullets  && !Roo.isTouch){
18070                 
18071                 var bullets = {
18072                     cls : 'carousel-bullets',
18073                     cn : []
18074                 };
18075                
18076                 if(this.bullets_cls){
18077                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18078                 }
18079                 
18080                 bullets.cn.push({
18081                     cls : 'clear'
18082                 });
18083                 
18084                 cfg.cn[0].cn.push(bullets);
18085             }
18086             
18087             if(this.showarrow){
18088                 cfg.cn[0].cn.push({
18089                     tag : 'div',
18090                     class : 'carousel-arrow',
18091                     cn : [
18092                         {
18093                             tag : 'div',
18094                             class : 'carousel-prev',
18095                             cn : [
18096                                 {
18097                                     tag : 'i',
18098                                     class : 'fa fa-chevron-left'
18099                                 }
18100                             ]
18101                         },
18102                         {
18103                             tag : 'div',
18104                             class : 'carousel-next',
18105                             cn : [
18106                                 {
18107                                     tag : 'i',
18108                                     class : 'fa fa-chevron-right'
18109                                 }
18110                             ]
18111                         }
18112                     ]
18113                 });
18114             }
18115             
18116         }
18117         
18118         return cfg;
18119     },
18120     
18121     initEvents:  function()
18122     {
18123 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18124 //            this.el.on("touchstart", this.onTouchStart, this);
18125 //        }
18126         
18127         if(this.autoslide){
18128             var _this = this;
18129             
18130             this.slideFn = window.setInterval(function() {
18131                 _this.showPanelNext();
18132             }, this.timer);
18133         }
18134         
18135         if(this.showarrow){
18136             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18137             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18138         }
18139         
18140         
18141     },
18142     
18143 //    onTouchStart : function(e, el, o)
18144 //    {
18145 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18146 //            return;
18147 //        }
18148 //        
18149 //        this.showPanelNext();
18150 //    },
18151     
18152     
18153     getChildContainer : function()
18154     {
18155         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18156     },
18157     
18158     /**
18159     * register a Navigation item
18160     * @param {Roo.bootstrap.NavItem} the navitem to add
18161     */
18162     register : function(item)
18163     {
18164         this.tabs.push( item);
18165         item.navId = this.navId; // not really needed..
18166         this.addBullet();
18167     
18168     },
18169     
18170     getActivePanel : function()
18171     {
18172         var r = false;
18173         Roo.each(this.tabs, function(t) {
18174             if (t.active) {
18175                 r = t;
18176                 return false;
18177             }
18178             return null;
18179         });
18180         return r;
18181         
18182     },
18183     getPanelByName : function(n)
18184     {
18185         var r = false;
18186         Roo.each(this.tabs, function(t) {
18187             if (t.tabId == n) {
18188                 r = t;
18189                 return false;
18190             }
18191             return null;
18192         });
18193         return r;
18194     },
18195     indexOfPanel : function(p)
18196     {
18197         var r = false;
18198         Roo.each(this.tabs, function(t,i) {
18199             if (t.tabId == p.tabId) {
18200                 r = i;
18201                 return false;
18202             }
18203             return null;
18204         });
18205         return r;
18206     },
18207     /**
18208      * show a specific panel
18209      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18210      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18211      */
18212     showPanel : function (pan)
18213     {
18214         if(this.transition || typeof(pan) == 'undefined'){
18215             Roo.log("waiting for the transitionend");
18216             return;
18217         }
18218         
18219         if (typeof(pan) == 'number') {
18220             pan = this.tabs[pan];
18221         }
18222         
18223         if (typeof(pan) == 'string') {
18224             pan = this.getPanelByName(pan);
18225         }
18226         
18227         var cur = this.getActivePanel();
18228         
18229         if(!pan || !cur){
18230             Roo.log('pan or acitve pan is undefined');
18231             return false;
18232         }
18233         
18234         if (pan.tabId == this.getActivePanel().tabId) {
18235             return true;
18236         }
18237         
18238         if (false === cur.fireEvent('beforedeactivate')) {
18239             return false;
18240         }
18241         
18242         if(this.bullets > 0 && !Roo.isTouch){
18243             this.setActiveBullet(this.indexOfPanel(pan));
18244         }
18245         
18246         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18247             
18248             this.transition = true;
18249             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18250             var lr = dir == 'next' ? 'left' : 'right';
18251             pan.el.addClass(dir); // or prev
18252             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18253             cur.el.addClass(lr); // or right
18254             pan.el.addClass(lr);
18255             
18256             var _this = this;
18257             cur.el.on('transitionend', function() {
18258                 Roo.log("trans end?");
18259                 
18260                 pan.el.removeClass([lr,dir]);
18261                 pan.setActive(true);
18262                 
18263                 cur.el.removeClass([lr]);
18264                 cur.setActive(false);
18265                 
18266                 _this.transition = false;
18267                 
18268             }, this, { single:  true } );
18269             
18270             return true;
18271         }
18272         
18273         cur.setActive(false);
18274         pan.setActive(true);
18275         
18276         return true;
18277         
18278     },
18279     showPanelNext : function()
18280     {
18281         var i = this.indexOfPanel(this.getActivePanel());
18282         
18283         if (i >= this.tabs.length - 1 && !this.autoslide) {
18284             return;
18285         }
18286         
18287         if (i >= this.tabs.length - 1 && this.autoslide) {
18288             i = -1;
18289         }
18290         
18291         this.showPanel(this.tabs[i+1]);
18292     },
18293     
18294     showPanelPrev : function()
18295     {
18296         var i = this.indexOfPanel(this.getActivePanel());
18297         
18298         if (i  < 1 && !this.autoslide) {
18299             return;
18300         }
18301         
18302         if (i < 1 && this.autoslide) {
18303             i = this.tabs.length;
18304         }
18305         
18306         this.showPanel(this.tabs[i-1]);
18307     },
18308     
18309     
18310     addBullet: function()
18311     {
18312         if(!this.bullets || Roo.isTouch){
18313             return;
18314         }
18315         var ctr = this.el.select('.carousel-bullets',true).first();
18316         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18317         var bullet = ctr.createChild({
18318             cls : 'bullet bullet-' + i
18319         },ctr.dom.lastChild);
18320         
18321         
18322         var _this = this;
18323         
18324         bullet.on('click', (function(e, el, o, ii, t){
18325
18326             e.preventDefault();
18327
18328             this.showPanel(ii);
18329
18330             if(this.autoslide && this.slideFn){
18331                 clearInterval(this.slideFn);
18332                 this.slideFn = window.setInterval(function() {
18333                     _this.showPanelNext();
18334                 }, this.timer);
18335             }
18336
18337         }).createDelegate(this, [i, bullet], true));
18338                 
18339         
18340     },
18341      
18342     setActiveBullet : function(i)
18343     {
18344         if(Roo.isTouch){
18345             return;
18346         }
18347         
18348         Roo.each(this.el.select('.bullet', true).elements, function(el){
18349             el.removeClass('selected');
18350         });
18351
18352         var bullet = this.el.select('.bullet-' + i, true).first();
18353         
18354         if(!bullet){
18355             return;
18356         }
18357         
18358         bullet.addClass('selected');
18359     }
18360     
18361     
18362   
18363 });
18364
18365  
18366
18367  
18368  
18369 Roo.apply(Roo.bootstrap.TabGroup, {
18370     
18371     groups: {},
18372      /**
18373     * register a Navigation Group
18374     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18375     */
18376     register : function(navgrp)
18377     {
18378         this.groups[navgrp.navId] = navgrp;
18379         
18380     },
18381     /**
18382     * fetch a Navigation Group based on the navigation ID
18383     * if one does not exist , it will get created.
18384     * @param {string} the navgroup to add
18385     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18386     */
18387     get: function(navId) {
18388         if (typeof(this.groups[navId]) == 'undefined') {
18389             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18390         }
18391         return this.groups[navId] ;
18392     }
18393     
18394     
18395     
18396 });
18397
18398  /*
18399  * - LGPL
18400  *
18401  * TabPanel
18402  * 
18403  */
18404
18405 /**
18406  * @class Roo.bootstrap.TabPanel
18407  * @extends Roo.bootstrap.Component
18408  * Bootstrap TabPanel class
18409  * @cfg {Boolean} active panel active
18410  * @cfg {String} html panel content
18411  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18412  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18413  * @cfg {String} href click to link..
18414  * 
18415  * 
18416  * @constructor
18417  * Create a new TabPanel
18418  * @param {Object} config The config object
18419  */
18420
18421 Roo.bootstrap.TabPanel = function(config){
18422     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18423     this.addEvents({
18424         /**
18425              * @event changed
18426              * Fires when the active status changes
18427              * @param {Roo.bootstrap.TabPanel} this
18428              * @param {Boolean} state the new state
18429             
18430          */
18431         'changed': true,
18432         /**
18433              * @event beforedeactivate
18434              * Fires before a tab is de-activated - can be used to do validation on a form.
18435              * @param {Roo.bootstrap.TabPanel} this
18436              * @return {Boolean} false if there is an error
18437             
18438          */
18439         'beforedeactivate': true
18440      });
18441     
18442     this.tabId = this.tabId || Roo.id();
18443   
18444 };
18445
18446 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18447     
18448     active: false,
18449     html: false,
18450     tabId: false,
18451     navId : false,
18452     href : '',
18453     
18454     getAutoCreate : function(){
18455         var cfg = {
18456             tag: 'div',
18457             // item is needed for carousel - not sure if it has any effect otherwise
18458             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18459             html: this.html || ''
18460         };
18461         
18462         if(this.active){
18463             cfg.cls += ' active';
18464         }
18465         
18466         if(this.tabId){
18467             cfg.tabId = this.tabId;
18468         }
18469         
18470         
18471         return cfg;
18472     },
18473     
18474     initEvents:  function()
18475     {
18476         var p = this.parent();
18477         
18478         this.navId = this.navId || p.navId;
18479         
18480         if (typeof(this.navId) != 'undefined') {
18481             // not really needed.. but just in case.. parent should be a NavGroup.
18482             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18483             
18484             tg.register(this);
18485             
18486             var i = tg.tabs.length - 1;
18487             
18488             if(this.active && tg.bullets > 0 && i < tg.bullets){
18489                 tg.setActiveBullet(i);
18490             }
18491         }
18492         
18493         this.el.on('click', this.onClick, this);
18494         
18495         if(Roo.isTouch){
18496             this.el.on("touchstart", this.onTouchStart, this);
18497             this.el.on("touchmove", this.onTouchMove, this);
18498             this.el.on("touchend", this.onTouchEnd, this);
18499         }
18500         
18501     },
18502     
18503     onRender : function(ct, position)
18504     {
18505         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18506     },
18507     
18508     setActive : function(state)
18509     {
18510         Roo.log("panel - set active " + this.tabId + "=" + state);
18511         
18512         this.active = state;
18513         if (!state) {
18514             this.el.removeClass('active');
18515             
18516         } else  if (!this.el.hasClass('active')) {
18517             this.el.addClass('active');
18518         }
18519         
18520         this.fireEvent('changed', this, state);
18521     },
18522     
18523     onClick : function(e)
18524     {
18525         e.preventDefault();
18526         
18527         if(!this.href.length){
18528             return;
18529         }
18530         
18531         window.location.href = this.href;
18532     },
18533     
18534     startX : 0,
18535     startY : 0,
18536     endX : 0,
18537     endY : 0,
18538     swiping : false,
18539     
18540     onTouchStart : function(e)
18541     {
18542         this.swiping = false;
18543         
18544         this.startX = e.browserEvent.touches[0].clientX;
18545         this.startY = e.browserEvent.touches[0].clientY;
18546     },
18547     
18548     onTouchMove : function(e)
18549     {
18550         this.swiping = true;
18551         
18552         this.endX = e.browserEvent.touches[0].clientX;
18553         this.endY = e.browserEvent.touches[0].clientY;
18554     },
18555     
18556     onTouchEnd : function(e)
18557     {
18558         if(!this.swiping){
18559             this.onClick(e);
18560             return;
18561         }
18562         
18563         var tabGroup = this.parent();
18564         
18565         if(this.endX > this.startX){ // swiping right
18566             tabGroup.showPanelPrev();
18567             return;
18568         }
18569         
18570         if(this.startX > this.endX){ // swiping left
18571             tabGroup.showPanelNext();
18572             return;
18573         }
18574     }
18575     
18576     
18577 });
18578  
18579
18580  
18581
18582  /*
18583  * - LGPL
18584  *
18585  * DateField
18586  * 
18587  */
18588
18589 /**
18590  * @class Roo.bootstrap.DateField
18591  * @extends Roo.bootstrap.Input
18592  * Bootstrap DateField class
18593  * @cfg {Number} weekStart default 0
18594  * @cfg {String} viewMode default empty, (months|years)
18595  * @cfg {String} minViewMode default empty, (months|years)
18596  * @cfg {Number} startDate default -Infinity
18597  * @cfg {Number} endDate default Infinity
18598  * @cfg {Boolean} todayHighlight default false
18599  * @cfg {Boolean} todayBtn default false
18600  * @cfg {Boolean} calendarWeeks default false
18601  * @cfg {Object} daysOfWeekDisabled default empty
18602  * @cfg {Boolean} singleMode default false (true | false)
18603  * 
18604  * @cfg {Boolean} keyboardNavigation default true
18605  * @cfg {String} language default en
18606  * 
18607  * @constructor
18608  * Create a new DateField
18609  * @param {Object} config The config object
18610  */
18611
18612 Roo.bootstrap.DateField = function(config){
18613     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18614      this.addEvents({
18615             /**
18616              * @event show
18617              * Fires when this field show.
18618              * @param {Roo.bootstrap.DateField} this
18619              * @param {Mixed} date The date value
18620              */
18621             show : true,
18622             /**
18623              * @event show
18624              * Fires when this field hide.
18625              * @param {Roo.bootstrap.DateField} this
18626              * @param {Mixed} date The date value
18627              */
18628             hide : true,
18629             /**
18630              * @event select
18631              * Fires when select a date.
18632              * @param {Roo.bootstrap.DateField} this
18633              * @param {Mixed} date The date value
18634              */
18635             select : true,
18636             /**
18637              * @event beforeselect
18638              * Fires when before select a date.
18639              * @param {Roo.bootstrap.DateField} this
18640              * @param {Mixed} date The date value
18641              */
18642             beforeselect : true
18643         });
18644 };
18645
18646 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18647     
18648     /**
18649      * @cfg {String} format
18650      * The default date format string which can be overriden for localization support.  The format must be
18651      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18652      */
18653     format : "m/d/y",
18654     /**
18655      * @cfg {String} altFormats
18656      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18657      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18658      */
18659     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18660     
18661     weekStart : 0,
18662     
18663     viewMode : '',
18664     
18665     minViewMode : '',
18666     
18667     todayHighlight : false,
18668     
18669     todayBtn: false,
18670     
18671     language: 'en',
18672     
18673     keyboardNavigation: true,
18674     
18675     calendarWeeks: false,
18676     
18677     startDate: -Infinity,
18678     
18679     endDate: Infinity,
18680     
18681     daysOfWeekDisabled: [],
18682     
18683     _events: [],
18684     
18685     singleMode : false,
18686     
18687     UTCDate: function()
18688     {
18689         return new Date(Date.UTC.apply(Date, arguments));
18690     },
18691     
18692     UTCToday: function()
18693     {
18694         var today = new Date();
18695         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18696     },
18697     
18698     getDate: function() {
18699             var d = this.getUTCDate();
18700             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18701     },
18702     
18703     getUTCDate: function() {
18704             return this.date;
18705     },
18706     
18707     setDate: function(d) {
18708             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18709     },
18710     
18711     setUTCDate: function(d) {
18712             this.date = d;
18713             this.setValue(this.formatDate(this.date));
18714     },
18715         
18716     onRender: function(ct, position)
18717     {
18718         
18719         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18720         
18721         this.language = this.language || 'en';
18722         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18723         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18724         
18725         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18726         this.format = this.format || 'm/d/y';
18727         this.isInline = false;
18728         this.isInput = true;
18729         this.component = this.el.select('.add-on', true).first() || false;
18730         this.component = (this.component && this.component.length === 0) ? false : this.component;
18731         this.hasInput = this.component && this.inputEl().length;
18732         
18733         if (typeof(this.minViewMode === 'string')) {
18734             switch (this.minViewMode) {
18735                 case 'months':
18736                     this.minViewMode = 1;
18737                     break;
18738                 case 'years':
18739                     this.minViewMode = 2;
18740                     break;
18741                 default:
18742                     this.minViewMode = 0;
18743                     break;
18744             }
18745         }
18746         
18747         if (typeof(this.viewMode === 'string')) {
18748             switch (this.viewMode) {
18749                 case 'months':
18750                     this.viewMode = 1;
18751                     break;
18752                 case 'years':
18753                     this.viewMode = 2;
18754                     break;
18755                 default:
18756                     this.viewMode = 0;
18757                     break;
18758             }
18759         }
18760                 
18761         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18762         
18763 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18764         
18765         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18766         
18767         this.picker().on('mousedown', this.onMousedown, this);
18768         this.picker().on('click', this.onClick, this);
18769         
18770         this.picker().addClass('datepicker-dropdown');
18771         
18772         this.startViewMode = this.viewMode;
18773         
18774         if(this.singleMode){
18775             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18776                 v.setVisibilityMode(Roo.Element.DISPLAY);
18777                 v.hide();
18778             });
18779             
18780             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18781                 v.setStyle('width', '189px');
18782             });
18783         }
18784         
18785         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18786             if(!this.calendarWeeks){
18787                 v.remove();
18788                 return;
18789             }
18790             
18791             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18792             v.attr('colspan', function(i, val){
18793                 return parseInt(val) + 1;
18794             });
18795         });
18796                         
18797         
18798         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18799         
18800         this.setStartDate(this.startDate);
18801         this.setEndDate(this.endDate);
18802         
18803         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18804         
18805         this.fillDow();
18806         this.fillMonths();
18807         this.update();
18808         this.showMode();
18809         
18810         if(this.isInline) {
18811             this.showPopup();
18812         }
18813     },
18814     
18815     picker : function()
18816     {
18817         return this.pickerEl;
18818 //        return this.el.select('.datepicker', true).first();
18819     },
18820     
18821     fillDow: function()
18822     {
18823         var dowCnt = this.weekStart;
18824         
18825         var dow = {
18826             tag: 'tr',
18827             cn: [
18828                 
18829             ]
18830         };
18831         
18832         if(this.calendarWeeks){
18833             dow.cn.push({
18834                 tag: 'th',
18835                 cls: 'cw',
18836                 html: '&nbsp;'
18837             })
18838         }
18839         
18840         while (dowCnt < this.weekStart + 7) {
18841             dow.cn.push({
18842                 tag: 'th',
18843                 cls: 'dow',
18844                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18845             });
18846         }
18847         
18848         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18849     },
18850     
18851     fillMonths: function()
18852     {    
18853         var i = 0;
18854         var months = this.picker().select('>.datepicker-months td', true).first();
18855         
18856         months.dom.innerHTML = '';
18857         
18858         while (i < 12) {
18859             var month = {
18860                 tag: 'span',
18861                 cls: 'month',
18862                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18863             };
18864             
18865             months.createChild(month);
18866         }
18867         
18868     },
18869     
18870     update: function()
18871     {
18872         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;
18873         
18874         if (this.date < this.startDate) {
18875             this.viewDate = new Date(this.startDate);
18876         } else if (this.date > this.endDate) {
18877             this.viewDate = new Date(this.endDate);
18878         } else {
18879             this.viewDate = new Date(this.date);
18880         }
18881         
18882         this.fill();
18883     },
18884     
18885     fill: function() 
18886     {
18887         var d = new Date(this.viewDate),
18888                 year = d.getUTCFullYear(),
18889                 month = d.getUTCMonth(),
18890                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18891                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18892                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18893                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18894                 currentDate = this.date && this.date.valueOf(),
18895                 today = this.UTCToday();
18896         
18897         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18898         
18899 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18900         
18901 //        this.picker.select('>tfoot th.today').
18902 //                                              .text(dates[this.language].today)
18903 //                                              .toggle(this.todayBtn !== false);
18904     
18905         this.updateNavArrows();
18906         this.fillMonths();
18907                                                 
18908         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18909         
18910         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18911          
18912         prevMonth.setUTCDate(day);
18913         
18914         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18915         
18916         var nextMonth = new Date(prevMonth);
18917         
18918         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18919         
18920         nextMonth = nextMonth.valueOf();
18921         
18922         var fillMonths = false;
18923         
18924         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18925         
18926         while(prevMonth.valueOf() <= nextMonth) {
18927             var clsName = '';
18928             
18929             if (prevMonth.getUTCDay() === this.weekStart) {
18930                 if(fillMonths){
18931                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18932                 }
18933                     
18934                 fillMonths = {
18935                     tag: 'tr',
18936                     cn: []
18937                 };
18938                 
18939                 if(this.calendarWeeks){
18940                     // ISO 8601: First week contains first thursday.
18941                     // ISO also states week starts on Monday, but we can be more abstract here.
18942                     var
18943                     // Start of current week: based on weekstart/current date
18944                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18945                     // Thursday of this week
18946                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18947                     // First Thursday of year, year from thursday
18948                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18949                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18950                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18951                     
18952                     fillMonths.cn.push({
18953                         tag: 'td',
18954                         cls: 'cw',
18955                         html: calWeek
18956                     });
18957                 }
18958             }
18959             
18960             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18961                 clsName += ' old';
18962             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18963                 clsName += ' new';
18964             }
18965             if (this.todayHighlight &&
18966                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18967                 prevMonth.getUTCMonth() == today.getMonth() &&
18968                 prevMonth.getUTCDate() == today.getDate()) {
18969                 clsName += ' today';
18970             }
18971             
18972             if (currentDate && prevMonth.valueOf() === currentDate) {
18973                 clsName += ' active';
18974             }
18975             
18976             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18977                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18978                     clsName += ' disabled';
18979             }
18980             
18981             fillMonths.cn.push({
18982                 tag: 'td',
18983                 cls: 'day ' + clsName,
18984                 html: prevMonth.getDate()
18985             });
18986             
18987             prevMonth.setDate(prevMonth.getDate()+1);
18988         }
18989           
18990         var currentYear = this.date && this.date.getUTCFullYear();
18991         var currentMonth = this.date && this.date.getUTCMonth();
18992         
18993         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18994         
18995         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18996             v.removeClass('active');
18997             
18998             if(currentYear === year && k === currentMonth){
18999                 v.addClass('active');
19000             }
19001             
19002             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19003                 v.addClass('disabled');
19004             }
19005             
19006         });
19007         
19008         
19009         year = parseInt(year/10, 10) * 10;
19010         
19011         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19012         
19013         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19014         
19015         year -= 1;
19016         for (var i = -1; i < 11; i++) {
19017             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19018                 tag: 'span',
19019                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19020                 html: year
19021             });
19022             
19023             year += 1;
19024         }
19025     },
19026     
19027     showMode: function(dir) 
19028     {
19029         if (dir) {
19030             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19031         }
19032         
19033         Roo.each(this.picker().select('>div',true).elements, function(v){
19034             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19035             v.hide();
19036         });
19037         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19038     },
19039     
19040     place: function()
19041     {
19042         if(this.isInline) {
19043             return;
19044         }
19045         
19046         this.picker().removeClass(['bottom', 'top']);
19047         
19048         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19049             /*
19050              * place to the top of element!
19051              *
19052              */
19053             
19054             this.picker().addClass('top');
19055             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19056             
19057             return;
19058         }
19059         
19060         this.picker().addClass('bottom');
19061         
19062         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19063     },
19064     
19065     parseDate : function(value)
19066     {
19067         if(!value || value instanceof Date){
19068             return value;
19069         }
19070         var v = Date.parseDate(value, this.format);
19071         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19072             v = Date.parseDate(value, 'Y-m-d');
19073         }
19074         if(!v && this.altFormats){
19075             if(!this.altFormatsArray){
19076                 this.altFormatsArray = this.altFormats.split("|");
19077             }
19078             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19079                 v = Date.parseDate(value, this.altFormatsArray[i]);
19080             }
19081         }
19082         return v;
19083     },
19084     
19085     formatDate : function(date, fmt)
19086     {   
19087         return (!date || !(date instanceof Date)) ?
19088         date : date.dateFormat(fmt || this.format);
19089     },
19090     
19091     onFocus : function()
19092     {
19093         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19094         this.showPopup();
19095     },
19096     
19097     onBlur : function()
19098     {
19099         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19100         
19101         var d = this.inputEl().getValue();
19102         
19103         this.setValue(d);
19104                 
19105         this.hidePopup();
19106     },
19107     
19108     showPopup : function()
19109     {
19110         this.picker().show();
19111         this.update();
19112         this.place();
19113         
19114         this.fireEvent('showpopup', this, this.date);
19115     },
19116     
19117     hidePopup : function()
19118     {
19119         if(this.isInline) {
19120             return;
19121         }
19122         this.picker().hide();
19123         this.viewMode = this.startViewMode;
19124         this.showMode();
19125         
19126         this.fireEvent('hidepopup', this, this.date);
19127         
19128     },
19129     
19130     onMousedown: function(e)
19131     {
19132         e.stopPropagation();
19133         e.preventDefault();
19134     },
19135     
19136     keyup: function(e)
19137     {
19138         Roo.bootstrap.DateField.superclass.keyup.call(this);
19139         this.update();
19140     },
19141
19142     setValue: function(v)
19143     {
19144         if(this.fireEvent('beforeselect', this, v) !== false){
19145             var d = new Date(this.parseDate(v) ).clearTime();
19146         
19147             if(isNaN(d.getTime())){
19148                 this.date = this.viewDate = '';
19149                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19150                 return;
19151             }
19152
19153             v = this.formatDate(d);
19154
19155             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19156
19157             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19158
19159             this.update();
19160
19161             this.fireEvent('select', this, this.date);
19162         }
19163     },
19164     
19165     getValue: function()
19166     {
19167         return this.formatDate(this.date);
19168     },
19169     
19170     fireKey: function(e)
19171     {
19172         if (!this.picker().isVisible()){
19173             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19174                 this.showPopup();
19175             }
19176             return;
19177         }
19178         
19179         var dateChanged = false,
19180         dir, day, month,
19181         newDate, newViewDate;
19182         
19183         switch(e.keyCode){
19184             case 27: // escape
19185                 this.hidePopup();
19186                 e.preventDefault();
19187                 break;
19188             case 37: // left
19189             case 39: // right
19190                 if (!this.keyboardNavigation) {
19191                     break;
19192                 }
19193                 dir = e.keyCode == 37 ? -1 : 1;
19194                 
19195                 if (e.ctrlKey){
19196                     newDate = this.moveYear(this.date, dir);
19197                     newViewDate = this.moveYear(this.viewDate, dir);
19198                 } else if (e.shiftKey){
19199                     newDate = this.moveMonth(this.date, dir);
19200                     newViewDate = this.moveMonth(this.viewDate, dir);
19201                 } else {
19202                     newDate = new Date(this.date);
19203                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19204                     newViewDate = new Date(this.viewDate);
19205                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19206                 }
19207                 if (this.dateWithinRange(newDate)){
19208                     this.date = newDate;
19209                     this.viewDate = newViewDate;
19210                     this.setValue(this.formatDate(this.date));
19211 //                    this.update();
19212                     e.preventDefault();
19213                     dateChanged = true;
19214                 }
19215                 break;
19216             case 38: // up
19217             case 40: // down
19218                 if (!this.keyboardNavigation) {
19219                     break;
19220                 }
19221                 dir = e.keyCode == 38 ? -1 : 1;
19222                 if (e.ctrlKey){
19223                     newDate = this.moveYear(this.date, dir);
19224                     newViewDate = this.moveYear(this.viewDate, dir);
19225                 } else if (e.shiftKey){
19226                     newDate = this.moveMonth(this.date, dir);
19227                     newViewDate = this.moveMonth(this.viewDate, dir);
19228                 } else {
19229                     newDate = new Date(this.date);
19230                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19231                     newViewDate = new Date(this.viewDate);
19232                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19233                 }
19234                 if (this.dateWithinRange(newDate)){
19235                     this.date = newDate;
19236                     this.viewDate = newViewDate;
19237                     this.setValue(this.formatDate(this.date));
19238 //                    this.update();
19239                     e.preventDefault();
19240                     dateChanged = true;
19241                 }
19242                 break;
19243             case 13: // enter
19244                 this.setValue(this.formatDate(this.date));
19245                 this.hidePopup();
19246                 e.preventDefault();
19247                 break;
19248             case 9: // tab
19249                 this.setValue(this.formatDate(this.date));
19250                 this.hidePopup();
19251                 break;
19252             case 16: // shift
19253             case 17: // ctrl
19254             case 18: // alt
19255                 break;
19256             default :
19257                 this.hidePopup();
19258                 
19259         }
19260     },
19261     
19262     
19263     onClick: function(e) 
19264     {
19265         e.stopPropagation();
19266         e.preventDefault();
19267         
19268         var target = e.getTarget();
19269         
19270         if(target.nodeName.toLowerCase() === 'i'){
19271             target = Roo.get(target).dom.parentNode;
19272         }
19273         
19274         var nodeName = target.nodeName;
19275         var className = target.className;
19276         var html = target.innerHTML;
19277         //Roo.log(nodeName);
19278         
19279         switch(nodeName.toLowerCase()) {
19280             case 'th':
19281                 switch(className) {
19282                     case 'switch':
19283                         this.showMode(1);
19284                         break;
19285                     case 'prev':
19286                     case 'next':
19287                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19288                         switch(this.viewMode){
19289                                 case 0:
19290                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19291                                         break;
19292                                 case 1:
19293                                 case 2:
19294                                         this.viewDate = this.moveYear(this.viewDate, dir);
19295                                         break;
19296                         }
19297                         this.fill();
19298                         break;
19299                     case 'today':
19300                         var date = new Date();
19301                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19302 //                        this.fill()
19303                         this.setValue(this.formatDate(this.date));
19304                         
19305                         this.hidePopup();
19306                         break;
19307                 }
19308                 break;
19309             case 'span':
19310                 if (className.indexOf('disabled') < 0) {
19311                     this.viewDate.setUTCDate(1);
19312                     if (className.indexOf('month') > -1) {
19313                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19314                     } else {
19315                         var year = parseInt(html, 10) || 0;
19316                         this.viewDate.setUTCFullYear(year);
19317                         
19318                     }
19319                     
19320                     if(this.singleMode){
19321                         this.setValue(this.formatDate(this.viewDate));
19322                         this.hidePopup();
19323                         return;
19324                     }
19325                     
19326                     this.showMode(-1);
19327                     this.fill();
19328                 }
19329                 break;
19330                 
19331             case 'td':
19332                 //Roo.log(className);
19333                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19334                     var day = parseInt(html, 10) || 1;
19335                     var year = this.viewDate.getUTCFullYear(),
19336                         month = this.viewDate.getUTCMonth();
19337
19338                     if (className.indexOf('old') > -1) {
19339                         if(month === 0 ){
19340                             month = 11;
19341                             year -= 1;
19342                         }else{
19343                             month -= 1;
19344                         }
19345                     } else if (className.indexOf('new') > -1) {
19346                         if (month == 11) {
19347                             month = 0;
19348                             year += 1;
19349                         } else {
19350                             month += 1;
19351                         }
19352                     }
19353                     //Roo.log([year,month,day]);
19354                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19355                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19356 //                    this.fill();
19357                     //Roo.log(this.formatDate(this.date));
19358                     this.setValue(this.formatDate(this.date));
19359                     this.hidePopup();
19360                 }
19361                 break;
19362         }
19363     },
19364     
19365     setStartDate: function(startDate)
19366     {
19367         this.startDate = startDate || -Infinity;
19368         if (this.startDate !== -Infinity) {
19369             this.startDate = this.parseDate(this.startDate);
19370         }
19371         this.update();
19372         this.updateNavArrows();
19373     },
19374
19375     setEndDate: function(endDate)
19376     {
19377         this.endDate = endDate || Infinity;
19378         if (this.endDate !== Infinity) {
19379             this.endDate = this.parseDate(this.endDate);
19380         }
19381         this.update();
19382         this.updateNavArrows();
19383     },
19384     
19385     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19386     {
19387         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19388         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19389             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19390         }
19391         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19392             return parseInt(d, 10);
19393         });
19394         this.update();
19395         this.updateNavArrows();
19396     },
19397     
19398     updateNavArrows: function() 
19399     {
19400         if(this.singleMode){
19401             return;
19402         }
19403         
19404         var d = new Date(this.viewDate),
19405         year = d.getUTCFullYear(),
19406         month = d.getUTCMonth();
19407         
19408         Roo.each(this.picker().select('.prev', true).elements, function(v){
19409             v.show();
19410             switch (this.viewMode) {
19411                 case 0:
19412
19413                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19414                         v.hide();
19415                     }
19416                     break;
19417                 case 1:
19418                 case 2:
19419                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19420                         v.hide();
19421                     }
19422                     break;
19423             }
19424         });
19425         
19426         Roo.each(this.picker().select('.next', true).elements, function(v){
19427             v.show();
19428             switch (this.viewMode) {
19429                 case 0:
19430
19431                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19432                         v.hide();
19433                     }
19434                     break;
19435                 case 1:
19436                 case 2:
19437                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19438                         v.hide();
19439                     }
19440                     break;
19441             }
19442         })
19443     },
19444     
19445     moveMonth: function(date, dir)
19446     {
19447         if (!dir) {
19448             return date;
19449         }
19450         var new_date = new Date(date.valueOf()),
19451         day = new_date.getUTCDate(),
19452         month = new_date.getUTCMonth(),
19453         mag = Math.abs(dir),
19454         new_month, test;
19455         dir = dir > 0 ? 1 : -1;
19456         if (mag == 1){
19457             test = dir == -1
19458             // If going back one month, make sure month is not current month
19459             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19460             ? function(){
19461                 return new_date.getUTCMonth() == month;
19462             }
19463             // If going forward one month, make sure month is as expected
19464             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19465             : function(){
19466                 return new_date.getUTCMonth() != new_month;
19467             };
19468             new_month = month + dir;
19469             new_date.setUTCMonth(new_month);
19470             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19471             if (new_month < 0 || new_month > 11) {
19472                 new_month = (new_month + 12) % 12;
19473             }
19474         } else {
19475             // For magnitudes >1, move one month at a time...
19476             for (var i=0; i<mag; i++) {
19477                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19478                 new_date = this.moveMonth(new_date, dir);
19479             }
19480             // ...then reset the day, keeping it in the new month
19481             new_month = new_date.getUTCMonth();
19482             new_date.setUTCDate(day);
19483             test = function(){
19484                 return new_month != new_date.getUTCMonth();
19485             };
19486         }
19487         // Common date-resetting loop -- if date is beyond end of month, make it
19488         // end of month
19489         while (test()){
19490             new_date.setUTCDate(--day);
19491             new_date.setUTCMonth(new_month);
19492         }
19493         return new_date;
19494     },
19495
19496     moveYear: function(date, dir)
19497     {
19498         return this.moveMonth(date, dir*12);
19499     },
19500
19501     dateWithinRange: function(date)
19502     {
19503         return date >= this.startDate && date <= this.endDate;
19504     },
19505
19506     
19507     remove: function() 
19508     {
19509         this.picker().remove();
19510     },
19511     
19512     validateValue : function(value)
19513     {
19514         if(this.getVisibilityEl().hasClass('hidden')){
19515             return true;
19516         }
19517         
19518         if(value.length < 1)  {
19519             if(this.allowBlank){
19520                 return true;
19521             }
19522             return false;
19523         }
19524         
19525         if(value.length < this.minLength){
19526             return false;
19527         }
19528         if(value.length > this.maxLength){
19529             return false;
19530         }
19531         if(this.vtype){
19532             var vt = Roo.form.VTypes;
19533             if(!vt[this.vtype](value, this)){
19534                 return false;
19535             }
19536         }
19537         if(typeof this.validator == "function"){
19538             var msg = this.validator(value);
19539             if(msg !== true){
19540                 return false;
19541             }
19542         }
19543         
19544         if(this.regex && !this.regex.test(value)){
19545             return false;
19546         }
19547         
19548         if(typeof(this.parseDate(value)) == 'undefined'){
19549             return false;
19550         }
19551         
19552         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19553             return false;
19554         }      
19555         
19556         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19557             return false;
19558         } 
19559         
19560         
19561         return true;
19562     },
19563     
19564     reset : function()
19565     {
19566         this.date = this.viewDate = '';
19567         
19568         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19569     }
19570    
19571 });
19572
19573 Roo.apply(Roo.bootstrap.DateField,  {
19574     
19575     head : {
19576         tag: 'thead',
19577         cn: [
19578         {
19579             tag: 'tr',
19580             cn: [
19581             {
19582                 tag: 'th',
19583                 cls: 'prev',
19584                 html: '<i class="fa fa-arrow-left"/>'
19585             },
19586             {
19587                 tag: 'th',
19588                 cls: 'switch',
19589                 colspan: '5'
19590             },
19591             {
19592                 tag: 'th',
19593                 cls: 'next',
19594                 html: '<i class="fa fa-arrow-right"/>'
19595             }
19596
19597             ]
19598         }
19599         ]
19600     },
19601     
19602     content : {
19603         tag: 'tbody',
19604         cn: [
19605         {
19606             tag: 'tr',
19607             cn: [
19608             {
19609                 tag: 'td',
19610                 colspan: '7'
19611             }
19612             ]
19613         }
19614         ]
19615     },
19616     
19617     footer : {
19618         tag: 'tfoot',
19619         cn: [
19620         {
19621             tag: 'tr',
19622             cn: [
19623             {
19624                 tag: 'th',
19625                 colspan: '7',
19626                 cls: 'today'
19627             }
19628                     
19629             ]
19630         }
19631         ]
19632     },
19633     
19634     dates:{
19635         en: {
19636             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19637             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19638             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19639             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19640             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19641             today: "Today"
19642         }
19643     },
19644     
19645     modes: [
19646     {
19647         clsName: 'days',
19648         navFnc: 'Month',
19649         navStep: 1
19650     },
19651     {
19652         clsName: 'months',
19653         navFnc: 'FullYear',
19654         navStep: 1
19655     },
19656     {
19657         clsName: 'years',
19658         navFnc: 'FullYear',
19659         navStep: 10
19660     }]
19661 });
19662
19663 Roo.apply(Roo.bootstrap.DateField,  {
19664   
19665     template : {
19666         tag: 'div',
19667         cls: 'datepicker dropdown-menu roo-dynamic',
19668         cn: [
19669         {
19670             tag: 'div',
19671             cls: 'datepicker-days',
19672             cn: [
19673             {
19674                 tag: 'table',
19675                 cls: 'table-condensed',
19676                 cn:[
19677                 Roo.bootstrap.DateField.head,
19678                 {
19679                     tag: 'tbody'
19680                 },
19681                 Roo.bootstrap.DateField.footer
19682                 ]
19683             }
19684             ]
19685         },
19686         {
19687             tag: 'div',
19688             cls: 'datepicker-months',
19689             cn: [
19690             {
19691                 tag: 'table',
19692                 cls: 'table-condensed',
19693                 cn:[
19694                 Roo.bootstrap.DateField.head,
19695                 Roo.bootstrap.DateField.content,
19696                 Roo.bootstrap.DateField.footer
19697                 ]
19698             }
19699             ]
19700         },
19701         {
19702             tag: 'div',
19703             cls: 'datepicker-years',
19704             cn: [
19705             {
19706                 tag: 'table',
19707                 cls: 'table-condensed',
19708                 cn:[
19709                 Roo.bootstrap.DateField.head,
19710                 Roo.bootstrap.DateField.content,
19711                 Roo.bootstrap.DateField.footer
19712                 ]
19713             }
19714             ]
19715         }
19716         ]
19717     }
19718 });
19719
19720  
19721
19722  /*
19723  * - LGPL
19724  *
19725  * TimeField
19726  * 
19727  */
19728
19729 /**
19730  * @class Roo.bootstrap.TimeField
19731  * @extends Roo.bootstrap.Input
19732  * Bootstrap DateField class
19733  * 
19734  * 
19735  * @constructor
19736  * Create a new TimeField
19737  * @param {Object} config The config object
19738  */
19739
19740 Roo.bootstrap.TimeField = function(config){
19741     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19742     this.addEvents({
19743             /**
19744              * @event show
19745              * Fires when this field show.
19746              * @param {Roo.bootstrap.DateField} thisthis
19747              * @param {Mixed} date The date value
19748              */
19749             show : true,
19750             /**
19751              * @event show
19752              * Fires when this field hide.
19753              * @param {Roo.bootstrap.DateField} this
19754              * @param {Mixed} date The date value
19755              */
19756             hide : true,
19757             /**
19758              * @event select
19759              * Fires when select a date.
19760              * @param {Roo.bootstrap.DateField} this
19761              * @param {Mixed} date The date value
19762              */
19763             select : true
19764         });
19765 };
19766
19767 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19768     
19769     /**
19770      * @cfg {String} format
19771      * The default time format string which can be overriden for localization support.  The format must be
19772      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19773      */
19774     format : "H:i",
19775        
19776     onRender: function(ct, position)
19777     {
19778         
19779         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19780                 
19781         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19782         
19783         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19784         
19785         this.pop = this.picker().select('>.datepicker-time',true).first();
19786         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19787         
19788         this.picker().on('mousedown', this.onMousedown, this);
19789         this.picker().on('click', this.onClick, this);
19790         
19791         this.picker().addClass('datepicker-dropdown');
19792     
19793         this.fillTime();
19794         this.update();
19795             
19796         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19797         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19798         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19799         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19800         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19801         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19802
19803     },
19804     
19805     fireKey: function(e){
19806         if (!this.picker().isVisible()){
19807             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19808                 this.show();
19809             }
19810             return;
19811         }
19812
19813         e.preventDefault();
19814         
19815         switch(e.keyCode){
19816             case 27: // escape
19817                 this.hide();
19818                 break;
19819             case 37: // left
19820             case 39: // right
19821                 this.onTogglePeriod();
19822                 break;
19823             case 38: // up
19824                 this.onIncrementMinutes();
19825                 break;
19826             case 40: // down
19827                 this.onDecrementMinutes();
19828                 break;
19829             case 13: // enter
19830             case 9: // tab
19831                 this.setTime();
19832                 break;
19833         }
19834     },
19835     
19836     onClick: function(e) {
19837         e.stopPropagation();
19838         e.preventDefault();
19839     },
19840     
19841     picker : function()
19842     {
19843         return this.el.select('.datepicker', true).first();
19844     },
19845     
19846     fillTime: function()
19847     {    
19848         var time = this.pop.select('tbody', true).first();
19849         
19850         time.dom.innerHTML = '';
19851         
19852         time.createChild({
19853             tag: 'tr',
19854             cn: [
19855                 {
19856                     tag: 'td',
19857                     cn: [
19858                         {
19859                             tag: 'a',
19860                             href: '#',
19861                             cls: 'btn',
19862                             cn: [
19863                                 {
19864                                     tag: 'span',
19865                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19866                                 }
19867                             ]
19868                         } 
19869                     ]
19870                 },
19871                 {
19872                     tag: 'td',
19873                     cls: 'separator'
19874                 },
19875                 {
19876                     tag: 'td',
19877                     cn: [
19878                         {
19879                             tag: 'a',
19880                             href: '#',
19881                             cls: 'btn',
19882                             cn: [
19883                                 {
19884                                     tag: 'span',
19885                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19886                                 }
19887                             ]
19888                         }
19889                     ]
19890                 },
19891                 {
19892                     tag: 'td',
19893                     cls: 'separator'
19894                 }
19895             ]
19896         });
19897         
19898         time.createChild({
19899             tag: 'tr',
19900             cn: [
19901                 {
19902                     tag: 'td',
19903                     cn: [
19904                         {
19905                             tag: 'span',
19906                             cls: 'timepicker-hour',
19907                             html: '00'
19908                         }  
19909                     ]
19910                 },
19911                 {
19912                     tag: 'td',
19913                     cls: 'separator',
19914                     html: ':'
19915                 },
19916                 {
19917                     tag: 'td',
19918                     cn: [
19919                         {
19920                             tag: 'span',
19921                             cls: 'timepicker-minute',
19922                             html: '00'
19923                         }  
19924                     ]
19925                 },
19926                 {
19927                     tag: 'td',
19928                     cls: 'separator'
19929                 },
19930                 {
19931                     tag: 'td',
19932                     cn: [
19933                         {
19934                             tag: 'button',
19935                             type: 'button',
19936                             cls: 'btn btn-primary period',
19937                             html: 'AM'
19938                             
19939                         }
19940                     ]
19941                 }
19942             ]
19943         });
19944         
19945         time.createChild({
19946             tag: 'tr',
19947             cn: [
19948                 {
19949                     tag: 'td',
19950                     cn: [
19951                         {
19952                             tag: 'a',
19953                             href: '#',
19954                             cls: 'btn',
19955                             cn: [
19956                                 {
19957                                     tag: 'span',
19958                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19959                                 }
19960                             ]
19961                         }
19962                     ]
19963                 },
19964                 {
19965                     tag: 'td',
19966                     cls: 'separator'
19967                 },
19968                 {
19969                     tag: 'td',
19970                     cn: [
19971                         {
19972                             tag: 'a',
19973                             href: '#',
19974                             cls: 'btn',
19975                             cn: [
19976                                 {
19977                                     tag: 'span',
19978                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19979                                 }
19980                             ]
19981                         }
19982                     ]
19983                 },
19984                 {
19985                     tag: 'td',
19986                     cls: 'separator'
19987                 }
19988             ]
19989         });
19990         
19991     },
19992     
19993     update: function()
19994     {
19995         
19996         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19997         
19998         this.fill();
19999     },
20000     
20001     fill: function() 
20002     {
20003         var hours = this.time.getHours();
20004         var minutes = this.time.getMinutes();
20005         var period = 'AM';
20006         
20007         if(hours > 11){
20008             period = 'PM';
20009         }
20010         
20011         if(hours == 0){
20012             hours = 12;
20013         }
20014         
20015         
20016         if(hours > 12){
20017             hours = hours - 12;
20018         }
20019         
20020         if(hours < 10){
20021             hours = '0' + hours;
20022         }
20023         
20024         if(minutes < 10){
20025             minutes = '0' + minutes;
20026         }
20027         
20028         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20029         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20030         this.pop.select('button', true).first().dom.innerHTML = period;
20031         
20032     },
20033     
20034     place: function()
20035     {   
20036         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20037         
20038         var cls = ['bottom'];
20039         
20040         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20041             cls.pop();
20042             cls.push('top');
20043         }
20044         
20045         cls.push('right');
20046         
20047         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20048             cls.pop();
20049             cls.push('left');
20050         }
20051         
20052         this.picker().addClass(cls.join('-'));
20053         
20054         var _this = this;
20055         
20056         Roo.each(cls, function(c){
20057             if(c == 'bottom'){
20058                 _this.picker().setTop(_this.inputEl().getHeight());
20059                 return;
20060             }
20061             if(c == 'top'){
20062                 _this.picker().setTop(0 - _this.picker().getHeight());
20063                 return;
20064             }
20065             
20066             if(c == 'left'){
20067                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20068                 return;
20069             }
20070             if(c == 'right'){
20071                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20072                 return;
20073             }
20074         });
20075         
20076     },
20077   
20078     onFocus : function()
20079     {
20080         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20081         this.show();
20082     },
20083     
20084     onBlur : function()
20085     {
20086         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20087         this.hide();
20088     },
20089     
20090     show : function()
20091     {
20092         this.picker().show();
20093         this.pop.show();
20094         this.update();
20095         this.place();
20096         
20097         this.fireEvent('show', this, this.date);
20098     },
20099     
20100     hide : function()
20101     {
20102         this.picker().hide();
20103         this.pop.hide();
20104         
20105         this.fireEvent('hide', this, this.date);
20106     },
20107     
20108     setTime : function()
20109     {
20110         this.hide();
20111         this.setValue(this.time.format(this.format));
20112         
20113         this.fireEvent('select', this, this.date);
20114         
20115         
20116     },
20117     
20118     onMousedown: function(e){
20119         e.stopPropagation();
20120         e.preventDefault();
20121     },
20122     
20123     onIncrementHours: function()
20124     {
20125         Roo.log('onIncrementHours');
20126         this.time = this.time.add(Date.HOUR, 1);
20127         this.update();
20128         
20129     },
20130     
20131     onDecrementHours: function()
20132     {
20133         Roo.log('onDecrementHours');
20134         this.time = this.time.add(Date.HOUR, -1);
20135         this.update();
20136     },
20137     
20138     onIncrementMinutes: function()
20139     {
20140         Roo.log('onIncrementMinutes');
20141         this.time = this.time.add(Date.MINUTE, 1);
20142         this.update();
20143     },
20144     
20145     onDecrementMinutes: function()
20146     {
20147         Roo.log('onDecrementMinutes');
20148         this.time = this.time.add(Date.MINUTE, -1);
20149         this.update();
20150     },
20151     
20152     onTogglePeriod: function()
20153     {
20154         Roo.log('onTogglePeriod');
20155         this.time = this.time.add(Date.HOUR, 12);
20156         this.update();
20157     }
20158     
20159    
20160 });
20161
20162 Roo.apply(Roo.bootstrap.TimeField,  {
20163     
20164     content : {
20165         tag: 'tbody',
20166         cn: [
20167             {
20168                 tag: 'tr',
20169                 cn: [
20170                 {
20171                     tag: 'td',
20172                     colspan: '7'
20173                 }
20174                 ]
20175             }
20176         ]
20177     },
20178     
20179     footer : {
20180         tag: 'tfoot',
20181         cn: [
20182             {
20183                 tag: 'tr',
20184                 cn: [
20185                 {
20186                     tag: 'th',
20187                     colspan: '7',
20188                     cls: '',
20189                     cn: [
20190                         {
20191                             tag: 'button',
20192                             cls: 'btn btn-info ok',
20193                             html: 'OK'
20194                         }
20195                     ]
20196                 }
20197
20198                 ]
20199             }
20200         ]
20201     }
20202 });
20203
20204 Roo.apply(Roo.bootstrap.TimeField,  {
20205   
20206     template : {
20207         tag: 'div',
20208         cls: 'datepicker dropdown-menu',
20209         cn: [
20210             {
20211                 tag: 'div',
20212                 cls: 'datepicker-time',
20213                 cn: [
20214                 {
20215                     tag: 'table',
20216                     cls: 'table-condensed',
20217                     cn:[
20218                     Roo.bootstrap.TimeField.content,
20219                     Roo.bootstrap.TimeField.footer
20220                     ]
20221                 }
20222                 ]
20223             }
20224         ]
20225     }
20226 });
20227
20228  
20229
20230  /*
20231  * - LGPL
20232  *
20233  * MonthField
20234  * 
20235  */
20236
20237 /**
20238  * @class Roo.bootstrap.MonthField
20239  * @extends Roo.bootstrap.Input
20240  * Bootstrap MonthField class
20241  * 
20242  * @cfg {String} language default en
20243  * 
20244  * @constructor
20245  * Create a new MonthField
20246  * @param {Object} config The config object
20247  */
20248
20249 Roo.bootstrap.MonthField = function(config){
20250     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20251     
20252     this.addEvents({
20253         /**
20254          * @event show
20255          * Fires when this field show.
20256          * @param {Roo.bootstrap.MonthField} this
20257          * @param {Mixed} date The date value
20258          */
20259         show : true,
20260         /**
20261          * @event show
20262          * Fires when this field hide.
20263          * @param {Roo.bootstrap.MonthField} this
20264          * @param {Mixed} date The date value
20265          */
20266         hide : true,
20267         /**
20268          * @event select
20269          * Fires when select a date.
20270          * @param {Roo.bootstrap.MonthField} this
20271          * @param {String} oldvalue The old value
20272          * @param {String} newvalue The new value
20273          */
20274         select : true
20275     });
20276 };
20277
20278 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20279     
20280     onRender: function(ct, position)
20281     {
20282         
20283         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20284         
20285         this.language = this.language || 'en';
20286         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20287         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20288         
20289         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20290         this.isInline = false;
20291         this.isInput = true;
20292         this.component = this.el.select('.add-on', true).first() || false;
20293         this.component = (this.component && this.component.length === 0) ? false : this.component;
20294         this.hasInput = this.component && this.inputEL().length;
20295         
20296         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20297         
20298         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20299         
20300         this.picker().on('mousedown', this.onMousedown, this);
20301         this.picker().on('click', this.onClick, this);
20302         
20303         this.picker().addClass('datepicker-dropdown');
20304         
20305         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20306             v.setStyle('width', '189px');
20307         });
20308         
20309         this.fillMonths();
20310         
20311         this.update();
20312         
20313         if(this.isInline) {
20314             this.show();
20315         }
20316         
20317     },
20318     
20319     setValue: function(v, suppressEvent)
20320     {   
20321         var o = this.getValue();
20322         
20323         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20324         
20325         this.update();
20326
20327         if(suppressEvent !== true){
20328             this.fireEvent('select', this, o, v);
20329         }
20330         
20331     },
20332     
20333     getValue: function()
20334     {
20335         return this.value;
20336     },
20337     
20338     onClick: function(e) 
20339     {
20340         e.stopPropagation();
20341         e.preventDefault();
20342         
20343         var target = e.getTarget();
20344         
20345         if(target.nodeName.toLowerCase() === 'i'){
20346             target = Roo.get(target).dom.parentNode;
20347         }
20348         
20349         var nodeName = target.nodeName;
20350         var className = target.className;
20351         var html = target.innerHTML;
20352         
20353         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20354             return;
20355         }
20356         
20357         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20358         
20359         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20360         
20361         this.hide();
20362                         
20363     },
20364     
20365     picker : function()
20366     {
20367         return this.pickerEl;
20368     },
20369     
20370     fillMonths: function()
20371     {    
20372         var i = 0;
20373         var months = this.picker().select('>.datepicker-months td', true).first();
20374         
20375         months.dom.innerHTML = '';
20376         
20377         while (i < 12) {
20378             var month = {
20379                 tag: 'span',
20380                 cls: 'month',
20381                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20382             };
20383             
20384             months.createChild(month);
20385         }
20386         
20387     },
20388     
20389     update: function()
20390     {
20391         var _this = this;
20392         
20393         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20394             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20395         }
20396         
20397         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20398             e.removeClass('active');
20399             
20400             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20401                 e.addClass('active');
20402             }
20403         })
20404     },
20405     
20406     place: function()
20407     {
20408         if(this.isInline) {
20409             return;
20410         }
20411         
20412         this.picker().removeClass(['bottom', 'top']);
20413         
20414         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20415             /*
20416              * place to the top of element!
20417              *
20418              */
20419             
20420             this.picker().addClass('top');
20421             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20422             
20423             return;
20424         }
20425         
20426         this.picker().addClass('bottom');
20427         
20428         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20429     },
20430     
20431     onFocus : function()
20432     {
20433         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20434         this.show();
20435     },
20436     
20437     onBlur : function()
20438     {
20439         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20440         
20441         var d = this.inputEl().getValue();
20442         
20443         this.setValue(d);
20444                 
20445         this.hide();
20446     },
20447     
20448     show : function()
20449     {
20450         this.picker().show();
20451         this.picker().select('>.datepicker-months', true).first().show();
20452         this.update();
20453         this.place();
20454         
20455         this.fireEvent('show', this, this.date);
20456     },
20457     
20458     hide : function()
20459     {
20460         if(this.isInline) {
20461             return;
20462         }
20463         this.picker().hide();
20464         this.fireEvent('hide', this, this.date);
20465         
20466     },
20467     
20468     onMousedown: function(e)
20469     {
20470         e.stopPropagation();
20471         e.preventDefault();
20472     },
20473     
20474     keyup: function(e)
20475     {
20476         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20477         this.update();
20478     },
20479
20480     fireKey: function(e)
20481     {
20482         if (!this.picker().isVisible()){
20483             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20484                 this.show();
20485             }
20486             return;
20487         }
20488         
20489         var dir;
20490         
20491         switch(e.keyCode){
20492             case 27: // escape
20493                 this.hide();
20494                 e.preventDefault();
20495                 break;
20496             case 37: // left
20497             case 39: // right
20498                 dir = e.keyCode == 37 ? -1 : 1;
20499                 
20500                 this.vIndex = this.vIndex + dir;
20501                 
20502                 if(this.vIndex < 0){
20503                     this.vIndex = 0;
20504                 }
20505                 
20506                 if(this.vIndex > 11){
20507                     this.vIndex = 11;
20508                 }
20509                 
20510                 if(isNaN(this.vIndex)){
20511                     this.vIndex = 0;
20512                 }
20513                 
20514                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20515                 
20516                 break;
20517             case 38: // up
20518             case 40: // down
20519                 
20520                 dir = e.keyCode == 38 ? -1 : 1;
20521                 
20522                 this.vIndex = this.vIndex + dir * 4;
20523                 
20524                 if(this.vIndex < 0){
20525                     this.vIndex = 0;
20526                 }
20527                 
20528                 if(this.vIndex > 11){
20529                     this.vIndex = 11;
20530                 }
20531                 
20532                 if(isNaN(this.vIndex)){
20533                     this.vIndex = 0;
20534                 }
20535                 
20536                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20537                 break;
20538                 
20539             case 13: // enter
20540                 
20541                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20542                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20543                 }
20544                 
20545                 this.hide();
20546                 e.preventDefault();
20547                 break;
20548             case 9: // tab
20549                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20550                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20551                 }
20552                 this.hide();
20553                 break;
20554             case 16: // shift
20555             case 17: // ctrl
20556             case 18: // alt
20557                 break;
20558             default :
20559                 this.hide();
20560                 
20561         }
20562     },
20563     
20564     remove: function() 
20565     {
20566         this.picker().remove();
20567     }
20568    
20569 });
20570
20571 Roo.apply(Roo.bootstrap.MonthField,  {
20572     
20573     content : {
20574         tag: 'tbody',
20575         cn: [
20576         {
20577             tag: 'tr',
20578             cn: [
20579             {
20580                 tag: 'td',
20581                 colspan: '7'
20582             }
20583             ]
20584         }
20585         ]
20586     },
20587     
20588     dates:{
20589         en: {
20590             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20591             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20592         }
20593     }
20594 });
20595
20596 Roo.apply(Roo.bootstrap.MonthField,  {
20597   
20598     template : {
20599         tag: 'div',
20600         cls: 'datepicker dropdown-menu roo-dynamic',
20601         cn: [
20602             {
20603                 tag: 'div',
20604                 cls: 'datepicker-months',
20605                 cn: [
20606                 {
20607                     tag: 'table',
20608                     cls: 'table-condensed',
20609                     cn:[
20610                         Roo.bootstrap.DateField.content
20611                     ]
20612                 }
20613                 ]
20614             }
20615         ]
20616     }
20617 });
20618
20619  
20620
20621  
20622  /*
20623  * - LGPL
20624  *
20625  * CheckBox
20626  * 
20627  */
20628
20629 /**
20630  * @class Roo.bootstrap.CheckBox
20631  * @extends Roo.bootstrap.Input
20632  * Bootstrap CheckBox class
20633  * 
20634  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20635  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20636  * @cfg {String} boxLabel The text that appears beside the checkbox
20637  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20638  * @cfg {Boolean} checked initnal the element
20639  * @cfg {Boolean} inline inline the element (default false)
20640  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20641  * @cfg {String} tooltip label tooltip
20642  * 
20643  * @constructor
20644  * Create a new CheckBox
20645  * @param {Object} config The config object
20646  */
20647
20648 Roo.bootstrap.CheckBox = function(config){
20649     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20650    
20651     this.addEvents({
20652         /**
20653         * @event check
20654         * Fires when the element is checked or unchecked.
20655         * @param {Roo.bootstrap.CheckBox} this This input
20656         * @param {Boolean} checked The new checked value
20657         */
20658        check : true,
20659        /**
20660         * @event click
20661         * Fires when the element is click.
20662         * @param {Roo.bootstrap.CheckBox} this This input
20663         */
20664        click : true
20665     });
20666     
20667 };
20668
20669 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20670   
20671     inputType: 'checkbox',
20672     inputValue: 1,
20673     valueOff: 0,
20674     boxLabel: false,
20675     checked: false,
20676     weight : false,
20677     inline: false,
20678     tooltip : '',
20679     
20680     getAutoCreate : function()
20681     {
20682         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20683         
20684         var id = Roo.id();
20685         
20686         var cfg = {};
20687         
20688         cfg.cls = 'form-group ' + this.inputType; //input-group
20689         
20690         if(this.inline){
20691             cfg.cls += ' ' + this.inputType + '-inline';
20692         }
20693         
20694         var input =  {
20695             tag: 'input',
20696             id : id,
20697             type : this.inputType,
20698             value : this.inputValue,
20699             cls : 'roo-' + this.inputType, //'form-box',
20700             placeholder : this.placeholder || ''
20701             
20702         };
20703         
20704         if(this.inputType != 'radio'){
20705             var hidden =  {
20706                 tag: 'input',
20707                 type : 'hidden',
20708                 cls : 'roo-hidden-value',
20709                 value : this.checked ? this.inputValue : this.valueOff
20710             };
20711         }
20712         
20713             
20714         if (this.weight) { // Validity check?
20715             cfg.cls += " " + this.inputType + "-" + this.weight;
20716         }
20717         
20718         if (this.disabled) {
20719             input.disabled=true;
20720         }
20721         
20722         if(this.checked){
20723             input.checked = this.checked;
20724         }
20725         
20726         if (this.name) {
20727             
20728             input.name = this.name;
20729             
20730             if(this.inputType != 'radio'){
20731                 hidden.name = this.name;
20732                 input.name = '_hidden_' + this.name;
20733             }
20734         }
20735         
20736         if (this.size) {
20737             input.cls += ' input-' + this.size;
20738         }
20739         
20740         var settings=this;
20741         
20742         ['xs','sm','md','lg'].map(function(size){
20743             if (settings[size]) {
20744                 cfg.cls += ' col-' + size + '-' + settings[size];
20745             }
20746         });
20747         
20748         var inputblock = input;
20749          
20750         if (this.before || this.after) {
20751             
20752             inputblock = {
20753                 cls : 'input-group',
20754                 cn :  [] 
20755             };
20756             
20757             if (this.before) {
20758                 inputblock.cn.push({
20759                     tag :'span',
20760                     cls : 'input-group-addon',
20761                     html : this.before
20762                 });
20763             }
20764             
20765             inputblock.cn.push(input);
20766             
20767             if(this.inputType != 'radio'){
20768                 inputblock.cn.push(hidden);
20769             }
20770             
20771             if (this.after) {
20772                 inputblock.cn.push({
20773                     tag :'span',
20774                     cls : 'input-group-addon',
20775                     html : this.after
20776                 });
20777             }
20778             
20779         }
20780         
20781         if (align ==='left' && this.fieldLabel.length) {
20782 //                Roo.log("left and has label");
20783             cfg.cn = [
20784                 {
20785                     tag: 'label',
20786                     'for' :  id,
20787                     cls : 'control-label',
20788                     html : this.fieldLabel
20789                 },
20790                 {
20791                     cls : "", 
20792                     cn: [
20793                         inputblock
20794                     ]
20795                 }
20796             ];
20797             
20798             if(this.labelWidth > 12){
20799                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20800             }
20801             
20802             if(this.labelWidth < 13 && this.labelmd == 0){
20803                 this.labelmd = this.labelWidth;
20804             }
20805             
20806             if(this.labellg > 0){
20807                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20808                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20809             }
20810             
20811             if(this.labelmd > 0){
20812                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20813                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20814             }
20815             
20816             if(this.labelsm > 0){
20817                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20818                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20819             }
20820             
20821             if(this.labelxs > 0){
20822                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20823                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20824             }
20825             
20826         } else if ( this.fieldLabel.length) {
20827 //                Roo.log(" label");
20828                 cfg.cn = [
20829                    
20830                     {
20831                         tag: this.boxLabel ? 'span' : 'label',
20832                         'for': id,
20833                         cls: 'control-label box-input-label',
20834                         //cls : 'input-group-addon',
20835                         html : this.fieldLabel
20836                     },
20837                     
20838                     inputblock
20839                     
20840                 ];
20841
20842         } else {
20843             
20844 //                Roo.log(" no label && no align");
20845                 cfg.cn = [  inputblock ] ;
20846                 
20847                 
20848         }
20849         
20850         if(this.boxLabel){
20851              var boxLabelCfg = {
20852                 tag: 'label',
20853                 //'for': id, // box label is handled by onclick - so no for...
20854                 cls: 'box-label',
20855                 html: this.boxLabel
20856             };
20857             
20858             if(this.tooltip){
20859                 boxLabelCfg.tooltip = this.tooltip;
20860             }
20861              
20862             cfg.cn.push(boxLabelCfg);
20863         }
20864         
20865         if(this.inputType != 'radio'){
20866             cfg.cn.push(hidden);
20867         }
20868         
20869         return cfg;
20870         
20871     },
20872     
20873     /**
20874      * return the real input element.
20875      */
20876     inputEl: function ()
20877     {
20878         return this.el.select('input.roo-' + this.inputType,true).first();
20879     },
20880     hiddenEl: function ()
20881     {
20882         return this.el.select('input.roo-hidden-value',true).first();
20883     },
20884     
20885     labelEl: function()
20886     {
20887         return this.el.select('label.control-label',true).first();
20888     },
20889     /* depricated... */
20890     
20891     label: function()
20892     {
20893         return this.labelEl();
20894     },
20895     
20896     boxLabelEl: function()
20897     {
20898         return this.el.select('label.box-label',true).first();
20899     },
20900     
20901     initEvents : function()
20902     {
20903 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20904         
20905         this.inputEl().on('click', this.onClick,  this);
20906         
20907         if (this.boxLabel) { 
20908             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20909         }
20910         
20911         this.startValue = this.getValue();
20912         
20913         if(this.groupId){
20914             Roo.bootstrap.CheckBox.register(this);
20915         }
20916     },
20917     
20918     onClick : function(e)
20919     {   
20920         if(this.fireEvent('click', this, e) !== false){
20921             this.setChecked(!this.checked);
20922         }
20923         
20924     },
20925     
20926     setChecked : function(state,suppressEvent)
20927     {
20928         this.startValue = this.getValue();
20929
20930         if(this.inputType == 'radio'){
20931             
20932             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20933                 e.dom.checked = false;
20934             });
20935             
20936             this.inputEl().dom.checked = true;
20937             
20938             this.inputEl().dom.value = this.inputValue;
20939             
20940             if(suppressEvent !== true){
20941                 this.fireEvent('check', this, true);
20942             }
20943             
20944             this.validate();
20945             
20946             return;
20947         }
20948         
20949         this.checked = state;
20950         
20951         this.inputEl().dom.checked = state;
20952         
20953         
20954         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20955         
20956         if(suppressEvent !== true){
20957             this.fireEvent('check', this, state);
20958         }
20959         
20960         this.validate();
20961     },
20962     
20963     getValue : function()
20964     {
20965         if(this.inputType == 'radio'){
20966             return this.getGroupValue();
20967         }
20968         
20969         return this.hiddenEl().dom.value;
20970         
20971     },
20972     
20973     getGroupValue : function()
20974     {
20975         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20976             return '';
20977         }
20978         
20979         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20980     },
20981     
20982     setValue : function(v,suppressEvent)
20983     {
20984         if(this.inputType == 'radio'){
20985             this.setGroupValue(v, suppressEvent);
20986             return;
20987         }
20988         
20989         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20990         
20991         this.validate();
20992     },
20993     
20994     setGroupValue : function(v, suppressEvent)
20995     {
20996         this.startValue = this.getValue();
20997         
20998         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20999             e.dom.checked = false;
21000             
21001             if(e.dom.value == v){
21002                 e.dom.checked = true;
21003             }
21004         });
21005         
21006         if(suppressEvent !== true){
21007             this.fireEvent('check', this, true);
21008         }
21009
21010         this.validate();
21011         
21012         return;
21013     },
21014     
21015     validate : function()
21016     {
21017         if(this.getVisibilityEl().hasClass('hidden')){
21018             return true;
21019         }
21020         
21021         if(
21022                 this.disabled || 
21023                 (this.inputType == 'radio' && this.validateRadio()) ||
21024                 (this.inputType == 'checkbox' && this.validateCheckbox())
21025         ){
21026             this.markValid();
21027             return true;
21028         }
21029         
21030         this.markInvalid();
21031         return false;
21032     },
21033     
21034     validateRadio : function()
21035     {
21036         if(this.getVisibilityEl().hasClass('hidden')){
21037             return true;
21038         }
21039         
21040         if(this.allowBlank){
21041             return true;
21042         }
21043         
21044         var valid = false;
21045         
21046         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21047             if(!e.dom.checked){
21048                 return;
21049             }
21050             
21051             valid = true;
21052             
21053             return false;
21054         });
21055         
21056         return valid;
21057     },
21058     
21059     validateCheckbox : function()
21060     {
21061         if(!this.groupId){
21062             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21063             //return (this.getValue() == this.inputValue) ? true : false;
21064         }
21065         
21066         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21067         
21068         if(!group){
21069             return false;
21070         }
21071         
21072         var r = false;
21073         
21074         for(var i in group){
21075             if(group[i].el.isVisible(true)){
21076                 r = false;
21077                 break;
21078             }
21079             
21080             r = true;
21081         }
21082         
21083         for(var i in group){
21084             if(r){
21085                 break;
21086             }
21087             
21088             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21089         }
21090         
21091         return r;
21092     },
21093     
21094     /**
21095      * Mark this field as valid
21096      */
21097     markValid : function()
21098     {
21099         var _this = this;
21100         
21101         this.fireEvent('valid', this);
21102         
21103         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21104         
21105         if(this.groupId){
21106             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21107         }
21108         
21109         if(label){
21110             label.markValid();
21111         }
21112
21113         if(this.inputType == 'radio'){
21114             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21115                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21116                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21117             });
21118             
21119             return;
21120         }
21121
21122         if(!this.groupId){
21123             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21124             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21125             return;
21126         }
21127         
21128         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21129         
21130         if(!group){
21131             return;
21132         }
21133         
21134         for(var i in group){
21135             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21136             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21137         }
21138     },
21139     
21140      /**
21141      * Mark this field as invalid
21142      * @param {String} msg The validation message
21143      */
21144     markInvalid : function(msg)
21145     {
21146         if(this.allowBlank){
21147             return;
21148         }
21149         
21150         var _this = this;
21151         
21152         this.fireEvent('invalid', this, msg);
21153         
21154         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21155         
21156         if(this.groupId){
21157             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21158         }
21159         
21160         if(label){
21161             label.markInvalid();
21162         }
21163             
21164         if(this.inputType == 'radio'){
21165             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21166                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21167                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21168             });
21169             
21170             return;
21171         }
21172         
21173         if(!this.groupId){
21174             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21175             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21176             return;
21177         }
21178         
21179         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21180         
21181         if(!group){
21182             return;
21183         }
21184         
21185         for(var i in group){
21186             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21187             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21188         }
21189         
21190     },
21191     
21192     clearInvalid : function()
21193     {
21194         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21195         
21196         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21197         
21198         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21199         
21200         if (label && label.iconEl) {
21201             label.iconEl.removeClass(label.validClass);
21202             label.iconEl.removeClass(label.invalidClass);
21203         }
21204     },
21205     
21206     disable : function()
21207     {
21208         if(this.inputType != 'radio'){
21209             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21210             return;
21211         }
21212         
21213         var _this = this;
21214         
21215         if(this.rendered){
21216             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21217                 _this.getActionEl().addClass(this.disabledClass);
21218                 e.dom.disabled = true;
21219             });
21220         }
21221         
21222         this.disabled = true;
21223         this.fireEvent("disable", this);
21224         return this;
21225     },
21226
21227     enable : function()
21228     {
21229         if(this.inputType != 'radio'){
21230             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21231             return;
21232         }
21233         
21234         var _this = this;
21235         
21236         if(this.rendered){
21237             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21238                 _this.getActionEl().removeClass(this.disabledClass);
21239                 e.dom.disabled = false;
21240             });
21241         }
21242         
21243         this.disabled = false;
21244         this.fireEvent("enable", this);
21245         return this;
21246     },
21247     
21248     setBoxLabel : function(v)
21249     {
21250         this.boxLabel = v;
21251         
21252         if(this.rendered){
21253             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21254         }
21255     }
21256
21257 });
21258
21259 Roo.apply(Roo.bootstrap.CheckBox, {
21260     
21261     groups: {},
21262     
21263      /**
21264     * register a CheckBox Group
21265     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21266     */
21267     register : function(checkbox)
21268     {
21269         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21270             this.groups[checkbox.groupId] = {};
21271         }
21272         
21273         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21274             return;
21275         }
21276         
21277         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21278         
21279     },
21280     /**
21281     * fetch a CheckBox Group based on the group ID
21282     * @param {string} the group ID
21283     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21284     */
21285     get: function(groupId) {
21286         if (typeof(this.groups[groupId]) == 'undefined') {
21287             return false;
21288         }
21289         
21290         return this.groups[groupId] ;
21291     }
21292     
21293     
21294 });
21295 /*
21296  * - LGPL
21297  *
21298  * RadioItem
21299  * 
21300  */
21301
21302 /**
21303  * @class Roo.bootstrap.Radio
21304  * @extends Roo.bootstrap.Component
21305  * Bootstrap Radio class
21306  * @cfg {String} boxLabel - the label associated
21307  * @cfg {String} value - the value of radio
21308  * 
21309  * @constructor
21310  * Create a new Radio
21311  * @param {Object} config The config object
21312  */
21313 Roo.bootstrap.Radio = function(config){
21314     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21315     
21316 };
21317
21318 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21319     
21320     boxLabel : '',
21321     
21322     value : '',
21323     
21324     getAutoCreate : function()
21325     {
21326         var cfg = {
21327             tag : 'div',
21328             cls : 'form-group radio',
21329             cn : [
21330                 {
21331                     tag : 'label',
21332                     cls : 'box-label',
21333                     html : this.boxLabel
21334                 }
21335             ]
21336         };
21337         
21338         return cfg;
21339     },
21340     
21341     initEvents : function() 
21342     {
21343         this.parent().register(this);
21344         
21345         this.el.on('click', this.onClick, this);
21346         
21347     },
21348     
21349     onClick : function(e)
21350     {
21351         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21352             this.setChecked(true);
21353         }
21354     },
21355     
21356     setChecked : function(state, suppressEvent)
21357     {
21358         this.parent().setValue(this.value, suppressEvent);
21359         
21360     },
21361     
21362     setBoxLabel : function(v)
21363     {
21364         this.boxLabel = v;
21365         
21366         if(this.rendered){
21367             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21368         }
21369     }
21370     
21371 });
21372  
21373
21374  /*
21375  * - LGPL
21376  *
21377  * Input
21378  * 
21379  */
21380
21381 /**
21382  * @class Roo.bootstrap.SecurePass
21383  * @extends Roo.bootstrap.Input
21384  * Bootstrap SecurePass class
21385  *
21386  * 
21387  * @constructor
21388  * Create a new SecurePass
21389  * @param {Object} config The config object
21390  */
21391  
21392 Roo.bootstrap.SecurePass = function (config) {
21393     // these go here, so the translation tool can replace them..
21394     this.errors = {
21395         PwdEmpty: "Please type a password, and then retype it to confirm.",
21396         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21397         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21398         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21399         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21400         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21401         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21402         TooWeak: "Your password is Too Weak."
21403     },
21404     this.meterLabel = "Password strength:";
21405     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21406     this.meterClass = [
21407         "roo-password-meter-tooweak", 
21408         "roo-password-meter-weak", 
21409         "roo-password-meter-medium", 
21410         "roo-password-meter-strong", 
21411         "roo-password-meter-grey"
21412     ];
21413     
21414     this.errors = {};
21415     
21416     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21417 }
21418
21419 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21420     /**
21421      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21422      * {
21423      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21424      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21425      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21426      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21427      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21428      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21429      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21430      * })
21431      */
21432     // private
21433     
21434     meterWidth: 300,
21435     errorMsg :'',    
21436     errors: false,
21437     imageRoot: '/',
21438     /**
21439      * @cfg {String/Object} Label for the strength meter (defaults to
21440      * 'Password strength:')
21441      */
21442     // private
21443     meterLabel: '',
21444     /**
21445      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21446      * ['Weak', 'Medium', 'Strong'])
21447      */
21448     // private    
21449     pwdStrengths: false,    
21450     // private
21451     strength: 0,
21452     // private
21453     _lastPwd: null,
21454     // private
21455     kCapitalLetter: 0,
21456     kSmallLetter: 1,
21457     kDigit: 2,
21458     kPunctuation: 3,
21459     
21460     insecure: false,
21461     // private
21462     initEvents: function ()
21463     {
21464         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21465
21466         if (this.el.is('input[type=password]') && Roo.isSafari) {
21467             this.el.on('keydown', this.SafariOnKeyDown, this);
21468         }
21469
21470         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21471     },
21472     // private
21473     onRender: function (ct, position)
21474     {
21475         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21476         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21477         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21478
21479         this.trigger.createChild({
21480                    cn: [
21481                     {
21482                     //id: 'PwdMeter',
21483                     tag: 'div',
21484                     cls: 'roo-password-meter-grey col-xs-12',
21485                     style: {
21486                         //width: 0,
21487                         //width: this.meterWidth + 'px'                                                
21488                         }
21489                     },
21490                     {                            
21491                          cls: 'roo-password-meter-text'                          
21492                     }
21493                 ]            
21494         });
21495
21496          
21497         if (this.hideTrigger) {
21498             this.trigger.setDisplayed(false);
21499         }
21500         this.setSize(this.width || '', this.height || '');
21501     },
21502     // private
21503     onDestroy: function ()
21504     {
21505         if (this.trigger) {
21506             this.trigger.removeAllListeners();
21507             this.trigger.remove();
21508         }
21509         if (this.wrap) {
21510             this.wrap.remove();
21511         }
21512         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21513     },
21514     // private
21515     checkStrength: function ()
21516     {
21517         var pwd = this.inputEl().getValue();
21518         if (pwd == this._lastPwd) {
21519             return;
21520         }
21521
21522         var strength;
21523         if (this.ClientSideStrongPassword(pwd)) {
21524             strength = 3;
21525         } else if (this.ClientSideMediumPassword(pwd)) {
21526             strength = 2;
21527         } else if (this.ClientSideWeakPassword(pwd)) {
21528             strength = 1;
21529         } else {
21530             strength = 0;
21531         }
21532         
21533         Roo.log('strength1: ' + strength);
21534         
21535         //var pm = this.trigger.child('div/div/div').dom;
21536         var pm = this.trigger.child('div/div');
21537         pm.removeClass(this.meterClass);
21538         pm.addClass(this.meterClass[strength]);
21539                 
21540         
21541         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21542                 
21543         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21544         
21545         this._lastPwd = pwd;
21546     },
21547     reset: function ()
21548     {
21549         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21550         
21551         this._lastPwd = '';
21552         
21553         var pm = this.trigger.child('div/div');
21554         pm.removeClass(this.meterClass);
21555         pm.addClass('roo-password-meter-grey');        
21556         
21557         
21558         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21559         
21560         pt.innerHTML = '';
21561         this.inputEl().dom.type='password';
21562     },
21563     // private
21564     validateValue: function (value)
21565     {
21566         
21567         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21568             return false;
21569         }
21570         if (value.length == 0) {
21571             if (this.allowBlank) {
21572                 this.clearInvalid();
21573                 return true;
21574             }
21575
21576             this.markInvalid(this.errors.PwdEmpty);
21577             this.errorMsg = this.errors.PwdEmpty;
21578             return false;
21579         }
21580         
21581         if(this.insecure){
21582             return true;
21583         }
21584         
21585         if ('[\x21-\x7e]*'.match(value)) {
21586             this.markInvalid(this.errors.PwdBadChar);
21587             this.errorMsg = this.errors.PwdBadChar;
21588             return false;
21589         }
21590         if (value.length < 6) {
21591             this.markInvalid(this.errors.PwdShort);
21592             this.errorMsg = this.errors.PwdShort;
21593             return false;
21594         }
21595         if (value.length > 16) {
21596             this.markInvalid(this.errors.PwdLong);
21597             this.errorMsg = this.errors.PwdLong;
21598             return false;
21599         }
21600         var strength;
21601         if (this.ClientSideStrongPassword(value)) {
21602             strength = 3;
21603         } else if (this.ClientSideMediumPassword(value)) {
21604             strength = 2;
21605         } else if (this.ClientSideWeakPassword(value)) {
21606             strength = 1;
21607         } else {
21608             strength = 0;
21609         }
21610
21611         
21612         if (strength < 2) {
21613             //this.markInvalid(this.errors.TooWeak);
21614             this.errorMsg = this.errors.TooWeak;
21615             //return false;
21616         }
21617         
21618         
21619         console.log('strength2: ' + strength);
21620         
21621         //var pm = this.trigger.child('div/div/div').dom;
21622         
21623         var pm = this.trigger.child('div/div');
21624         pm.removeClass(this.meterClass);
21625         pm.addClass(this.meterClass[strength]);
21626                 
21627         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21628                 
21629         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21630         
21631         this.errorMsg = ''; 
21632         return true;
21633     },
21634     // private
21635     CharacterSetChecks: function (type)
21636     {
21637         this.type = type;
21638         this.fResult = false;
21639     },
21640     // private
21641     isctype: function (character, type)
21642     {
21643         switch (type) {  
21644             case this.kCapitalLetter:
21645                 if (character >= 'A' && character <= 'Z') {
21646                     return true;
21647                 }
21648                 break;
21649             
21650             case this.kSmallLetter:
21651                 if (character >= 'a' && character <= 'z') {
21652                     return true;
21653                 }
21654                 break;
21655             
21656             case this.kDigit:
21657                 if (character >= '0' && character <= '9') {
21658                     return true;
21659                 }
21660                 break;
21661             
21662             case this.kPunctuation:
21663                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21664                     return true;
21665                 }
21666                 break;
21667             
21668             default:
21669                 return false;
21670         }
21671
21672     },
21673     // private
21674     IsLongEnough: function (pwd, size)
21675     {
21676         return !(pwd == null || isNaN(size) || pwd.length < size);
21677     },
21678     // private
21679     SpansEnoughCharacterSets: function (word, nb)
21680     {
21681         if (!this.IsLongEnough(word, nb))
21682         {
21683             return false;
21684         }
21685
21686         var characterSetChecks = new Array(
21687             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21688             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21689         );
21690         
21691         for (var index = 0; index < word.length; ++index) {
21692             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21693                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21694                     characterSetChecks[nCharSet].fResult = true;
21695                     break;
21696                 }
21697             }
21698         }
21699
21700         var nCharSets = 0;
21701         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21702             if (characterSetChecks[nCharSet].fResult) {
21703                 ++nCharSets;
21704             }
21705         }
21706
21707         if (nCharSets < nb) {
21708             return false;
21709         }
21710         return true;
21711     },
21712     // private
21713     ClientSideStrongPassword: function (pwd)
21714     {
21715         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21716     },
21717     // private
21718     ClientSideMediumPassword: function (pwd)
21719     {
21720         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21721     },
21722     // private
21723     ClientSideWeakPassword: function (pwd)
21724     {
21725         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21726     }
21727           
21728 })//<script type="text/javascript">
21729
21730 /*
21731  * Based  Ext JS Library 1.1.1
21732  * Copyright(c) 2006-2007, Ext JS, LLC.
21733  * LGPL
21734  *
21735  */
21736  
21737 /**
21738  * @class Roo.HtmlEditorCore
21739  * @extends Roo.Component
21740  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21741  *
21742  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21743  */
21744
21745 Roo.HtmlEditorCore = function(config){
21746     
21747     
21748     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21749     
21750     
21751     this.addEvents({
21752         /**
21753          * @event initialize
21754          * Fires when the editor is fully initialized (including the iframe)
21755          * @param {Roo.HtmlEditorCore} this
21756          */
21757         initialize: true,
21758         /**
21759          * @event activate
21760          * Fires when the editor is first receives the focus. Any insertion must wait
21761          * until after this event.
21762          * @param {Roo.HtmlEditorCore} this
21763          */
21764         activate: true,
21765          /**
21766          * @event beforesync
21767          * Fires before the textarea is updated with content from the editor iframe. Return false
21768          * to cancel the sync.
21769          * @param {Roo.HtmlEditorCore} this
21770          * @param {String} html
21771          */
21772         beforesync: true,
21773          /**
21774          * @event beforepush
21775          * Fires before the iframe editor is updated with content from the textarea. Return false
21776          * to cancel the push.
21777          * @param {Roo.HtmlEditorCore} this
21778          * @param {String} html
21779          */
21780         beforepush: true,
21781          /**
21782          * @event sync
21783          * Fires when the textarea is updated with content from the editor iframe.
21784          * @param {Roo.HtmlEditorCore} this
21785          * @param {String} html
21786          */
21787         sync: true,
21788          /**
21789          * @event push
21790          * Fires when the iframe editor is updated with content from the textarea.
21791          * @param {Roo.HtmlEditorCore} this
21792          * @param {String} html
21793          */
21794         push: true,
21795         
21796         /**
21797          * @event editorevent
21798          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21799          * @param {Roo.HtmlEditorCore} this
21800          */
21801         editorevent: true
21802         
21803     });
21804     
21805     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21806     
21807     // defaults : white / black...
21808     this.applyBlacklists();
21809     
21810     
21811     
21812 };
21813
21814
21815 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21816
21817
21818      /**
21819      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21820      */
21821     
21822     owner : false,
21823     
21824      /**
21825      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21826      *                        Roo.resizable.
21827      */
21828     resizable : false,
21829      /**
21830      * @cfg {Number} height (in pixels)
21831      */   
21832     height: 300,
21833    /**
21834      * @cfg {Number} width (in pixels)
21835      */   
21836     width: 500,
21837     
21838     /**
21839      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21840      * 
21841      */
21842     stylesheets: false,
21843     
21844     // id of frame..
21845     frameId: false,
21846     
21847     // private properties
21848     validationEvent : false,
21849     deferHeight: true,
21850     initialized : false,
21851     activated : false,
21852     sourceEditMode : false,
21853     onFocus : Roo.emptyFn,
21854     iframePad:3,
21855     hideMode:'offsets',
21856     
21857     clearUp: true,
21858     
21859     // blacklist + whitelisted elements..
21860     black: false,
21861     white: false,
21862      
21863     bodyCls : '',
21864
21865     /**
21866      * Protected method that will not generally be called directly. It
21867      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21868      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21869      */
21870     getDocMarkup : function(){
21871         // body styles..
21872         var st = '';
21873         
21874         // inherit styels from page...?? 
21875         if (this.stylesheets === false) {
21876             
21877             Roo.get(document.head).select('style').each(function(node) {
21878                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21879             });
21880             
21881             Roo.get(document.head).select('link').each(function(node) { 
21882                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21883             });
21884             
21885         } else if (!this.stylesheets.length) {
21886                 // simple..
21887                 st = '<style type="text/css">' +
21888                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21889                    '</style>';
21890         } else { 
21891             st = '<style type="text/css">' +
21892                     this.stylesheets +
21893                 '</style>';
21894         }
21895         
21896         st +=  '<style type="text/css">' +
21897             'IMG { cursor: pointer } ' +
21898         '</style>';
21899
21900         var cls = 'roo-htmleditor-body';
21901         
21902         if(this.bodyCls.length){
21903             cls += ' ' + this.bodyCls;
21904         }
21905         
21906         return '<html><head>' + st  +
21907             //<style type="text/css">' +
21908             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21909             //'</style>' +
21910             ' </head><body class="' +  cls + '"></body></html>';
21911     },
21912
21913     // private
21914     onRender : function(ct, position)
21915     {
21916         var _t = this;
21917         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21918         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21919         
21920         
21921         this.el.dom.style.border = '0 none';
21922         this.el.dom.setAttribute('tabIndex', -1);
21923         this.el.addClass('x-hidden hide');
21924         
21925         
21926         
21927         if(Roo.isIE){ // fix IE 1px bogus margin
21928             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21929         }
21930        
21931         
21932         this.frameId = Roo.id();
21933         
21934          
21935         
21936         var iframe = this.owner.wrap.createChild({
21937             tag: 'iframe',
21938             cls: 'form-control', // bootstrap..
21939             id: this.frameId,
21940             name: this.frameId,
21941             frameBorder : 'no',
21942             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21943         }, this.el
21944         );
21945         
21946         
21947         this.iframe = iframe.dom;
21948
21949          this.assignDocWin();
21950         
21951         this.doc.designMode = 'on';
21952        
21953         this.doc.open();
21954         this.doc.write(this.getDocMarkup());
21955         this.doc.close();
21956
21957         
21958         var task = { // must defer to wait for browser to be ready
21959             run : function(){
21960                 //console.log("run task?" + this.doc.readyState);
21961                 this.assignDocWin();
21962                 if(this.doc.body || this.doc.readyState == 'complete'){
21963                     try {
21964                         this.doc.designMode="on";
21965                     } catch (e) {
21966                         return;
21967                     }
21968                     Roo.TaskMgr.stop(task);
21969                     this.initEditor.defer(10, this);
21970                 }
21971             },
21972             interval : 10,
21973             duration: 10000,
21974             scope: this
21975         };
21976         Roo.TaskMgr.start(task);
21977
21978     },
21979
21980     // private
21981     onResize : function(w, h)
21982     {
21983          Roo.log('resize: ' +w + ',' + h );
21984         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21985         if(!this.iframe){
21986             return;
21987         }
21988         if(typeof w == 'number'){
21989             
21990             this.iframe.style.width = w + 'px';
21991         }
21992         if(typeof h == 'number'){
21993             
21994             this.iframe.style.height = h + 'px';
21995             if(this.doc){
21996                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21997             }
21998         }
21999         
22000     },
22001
22002     /**
22003      * Toggles the editor between standard and source edit mode.
22004      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22005      */
22006     toggleSourceEdit : function(sourceEditMode){
22007         
22008         this.sourceEditMode = sourceEditMode === true;
22009         
22010         if(this.sourceEditMode){
22011  
22012             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22013             
22014         }else{
22015             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22016             //this.iframe.className = '';
22017             this.deferFocus();
22018         }
22019         //this.setSize(this.owner.wrap.getSize());
22020         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22021     },
22022
22023     
22024   
22025
22026     /**
22027      * Protected method that will not generally be called directly. If you need/want
22028      * custom HTML cleanup, this is the method you should override.
22029      * @param {String} html The HTML to be cleaned
22030      * return {String} The cleaned HTML
22031      */
22032     cleanHtml : function(html){
22033         html = String(html);
22034         if(html.length > 5){
22035             if(Roo.isSafari){ // strip safari nonsense
22036                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22037             }
22038         }
22039         if(html == '&nbsp;'){
22040             html = '';
22041         }
22042         return html;
22043     },
22044
22045     /**
22046      * HTML Editor -> Textarea
22047      * Protected method that will not generally be called directly. Syncs the contents
22048      * of the editor iframe with the textarea.
22049      */
22050     syncValue : function(){
22051         if(this.initialized){
22052             var bd = (this.doc.body || this.doc.documentElement);
22053             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22054             var html = bd.innerHTML;
22055             if(Roo.isSafari){
22056                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22057                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22058                 if(m && m[1]){
22059                     html = '<div style="'+m[0]+'">' + html + '</div>';
22060                 }
22061             }
22062             html = this.cleanHtml(html);
22063             // fix up the special chars.. normaly like back quotes in word...
22064             // however we do not want to do this with chinese..
22065             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22066                 var cc = b.charCodeAt();
22067                 if (
22068                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22069                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22070                     (cc >= 0xf900 && cc < 0xfb00 )
22071                 ) {
22072                         return b;
22073                 }
22074                 return "&#"+cc+";" 
22075             });
22076             if(this.owner.fireEvent('beforesync', this, html) !== false){
22077                 this.el.dom.value = html;
22078                 this.owner.fireEvent('sync', this, html);
22079             }
22080         }
22081     },
22082
22083     /**
22084      * Protected method that will not generally be called directly. Pushes the value of the textarea
22085      * into the iframe editor.
22086      */
22087     pushValue : function(){
22088         if(this.initialized){
22089             var v = this.el.dom.value.trim();
22090             
22091 //            if(v.length < 1){
22092 //                v = '&#160;';
22093 //            }
22094             
22095             if(this.owner.fireEvent('beforepush', this, v) !== false){
22096                 var d = (this.doc.body || this.doc.documentElement);
22097                 d.innerHTML = v;
22098                 this.cleanUpPaste();
22099                 this.el.dom.value = d.innerHTML;
22100                 this.owner.fireEvent('push', this, v);
22101             }
22102         }
22103     },
22104
22105     // private
22106     deferFocus : function(){
22107         this.focus.defer(10, this);
22108     },
22109
22110     // doc'ed in Field
22111     focus : function(){
22112         if(this.win && !this.sourceEditMode){
22113             this.win.focus();
22114         }else{
22115             this.el.focus();
22116         }
22117     },
22118     
22119     assignDocWin: function()
22120     {
22121         var iframe = this.iframe;
22122         
22123          if(Roo.isIE){
22124             this.doc = iframe.contentWindow.document;
22125             this.win = iframe.contentWindow;
22126         } else {
22127 //            if (!Roo.get(this.frameId)) {
22128 //                return;
22129 //            }
22130 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22131 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22132             
22133             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22134                 return;
22135             }
22136             
22137             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22138             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22139         }
22140     },
22141     
22142     // private
22143     initEditor : function(){
22144         //console.log("INIT EDITOR");
22145         this.assignDocWin();
22146         
22147         
22148         
22149         this.doc.designMode="on";
22150         this.doc.open();
22151         this.doc.write(this.getDocMarkup());
22152         this.doc.close();
22153         
22154         var dbody = (this.doc.body || this.doc.documentElement);
22155         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22156         // this copies styles from the containing element into thsi one..
22157         // not sure why we need all of this..
22158         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22159         
22160         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22161         //ss['background-attachment'] = 'fixed'; // w3c
22162         dbody.bgProperties = 'fixed'; // ie
22163         //Roo.DomHelper.applyStyles(dbody, ss);
22164         Roo.EventManager.on(this.doc, {
22165             //'mousedown': this.onEditorEvent,
22166             'mouseup': this.onEditorEvent,
22167             'dblclick': this.onEditorEvent,
22168             'click': this.onEditorEvent,
22169             'keyup': this.onEditorEvent,
22170             buffer:100,
22171             scope: this
22172         });
22173         if(Roo.isGecko){
22174             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22175         }
22176         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22177             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22178         }
22179         this.initialized = true;
22180
22181         this.owner.fireEvent('initialize', this);
22182         this.pushValue();
22183     },
22184
22185     // private
22186     onDestroy : function(){
22187         
22188         
22189         
22190         if(this.rendered){
22191             
22192             //for (var i =0; i < this.toolbars.length;i++) {
22193             //    // fixme - ask toolbars for heights?
22194             //    this.toolbars[i].onDestroy();
22195            // }
22196             
22197             //this.wrap.dom.innerHTML = '';
22198             //this.wrap.remove();
22199         }
22200     },
22201
22202     // private
22203     onFirstFocus : function(){
22204         
22205         this.assignDocWin();
22206         
22207         
22208         this.activated = true;
22209          
22210     
22211         if(Roo.isGecko){ // prevent silly gecko errors
22212             this.win.focus();
22213             var s = this.win.getSelection();
22214             if(!s.focusNode || s.focusNode.nodeType != 3){
22215                 var r = s.getRangeAt(0);
22216                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22217                 r.collapse(true);
22218                 this.deferFocus();
22219             }
22220             try{
22221                 this.execCmd('useCSS', true);
22222                 this.execCmd('styleWithCSS', false);
22223             }catch(e){}
22224         }
22225         this.owner.fireEvent('activate', this);
22226     },
22227
22228     // private
22229     adjustFont: function(btn){
22230         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22231         //if(Roo.isSafari){ // safari
22232         //    adjust *= 2;
22233        // }
22234         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22235         if(Roo.isSafari){ // safari
22236             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22237             v =  (v < 10) ? 10 : v;
22238             v =  (v > 48) ? 48 : v;
22239             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22240             
22241         }
22242         
22243         
22244         v = Math.max(1, v+adjust);
22245         
22246         this.execCmd('FontSize', v  );
22247     },
22248
22249     onEditorEvent : function(e)
22250     {
22251         this.owner.fireEvent('editorevent', this, e);
22252       //  this.updateToolbar();
22253         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22254     },
22255
22256     insertTag : function(tg)
22257     {
22258         // could be a bit smarter... -> wrap the current selected tRoo..
22259         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22260             
22261             range = this.createRange(this.getSelection());
22262             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22263             wrappingNode.appendChild(range.extractContents());
22264             range.insertNode(wrappingNode);
22265
22266             return;
22267             
22268             
22269             
22270         }
22271         this.execCmd("formatblock",   tg);
22272         
22273     },
22274     
22275     insertText : function(txt)
22276     {
22277         
22278         
22279         var range = this.createRange();
22280         range.deleteContents();
22281                //alert(Sender.getAttribute('label'));
22282                
22283         range.insertNode(this.doc.createTextNode(txt));
22284     } ,
22285     
22286      
22287
22288     /**
22289      * Executes a Midas editor command on the editor document and performs necessary focus and
22290      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22291      * @param {String} cmd The Midas command
22292      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22293      */
22294     relayCmd : function(cmd, value){
22295         this.win.focus();
22296         this.execCmd(cmd, value);
22297         this.owner.fireEvent('editorevent', this);
22298         //this.updateToolbar();
22299         this.owner.deferFocus();
22300     },
22301
22302     /**
22303      * Executes a Midas editor command directly on the editor document.
22304      * For visual commands, you should use {@link #relayCmd} instead.
22305      * <b>This should only be called after the editor is initialized.</b>
22306      * @param {String} cmd The Midas command
22307      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22308      */
22309     execCmd : function(cmd, value){
22310         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22311         this.syncValue();
22312     },
22313  
22314  
22315    
22316     /**
22317      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22318      * to insert tRoo.
22319      * @param {String} text | dom node.. 
22320      */
22321     insertAtCursor : function(text)
22322     {
22323         
22324         if(!this.activated){
22325             return;
22326         }
22327         /*
22328         if(Roo.isIE){
22329             this.win.focus();
22330             var r = this.doc.selection.createRange();
22331             if(r){
22332                 r.collapse(true);
22333                 r.pasteHTML(text);
22334                 this.syncValue();
22335                 this.deferFocus();
22336             
22337             }
22338             return;
22339         }
22340         */
22341         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22342             this.win.focus();
22343             
22344             
22345             // from jquery ui (MIT licenced)
22346             var range, node;
22347             var win = this.win;
22348             
22349             if (win.getSelection && win.getSelection().getRangeAt) {
22350                 range = win.getSelection().getRangeAt(0);
22351                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22352                 range.insertNode(node);
22353             } else if (win.document.selection && win.document.selection.createRange) {
22354                 // no firefox support
22355                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22356                 win.document.selection.createRange().pasteHTML(txt);
22357             } else {
22358                 // no firefox support
22359                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22360                 this.execCmd('InsertHTML', txt);
22361             } 
22362             
22363             this.syncValue();
22364             
22365             this.deferFocus();
22366         }
22367     },
22368  // private
22369     mozKeyPress : function(e){
22370         if(e.ctrlKey){
22371             var c = e.getCharCode(), cmd;
22372           
22373             if(c > 0){
22374                 c = String.fromCharCode(c).toLowerCase();
22375                 switch(c){
22376                     case 'b':
22377                         cmd = 'bold';
22378                         break;
22379                     case 'i':
22380                         cmd = 'italic';
22381                         break;
22382                     
22383                     case 'u':
22384                         cmd = 'underline';
22385                         break;
22386                     
22387                     case 'v':
22388                         this.cleanUpPaste.defer(100, this);
22389                         return;
22390                         
22391                 }
22392                 if(cmd){
22393                     this.win.focus();
22394                     this.execCmd(cmd);
22395                     this.deferFocus();
22396                     e.preventDefault();
22397                 }
22398                 
22399             }
22400         }
22401     },
22402
22403     // private
22404     fixKeys : function(){ // load time branching for fastest keydown performance
22405         if(Roo.isIE){
22406             return function(e){
22407                 var k = e.getKey(), r;
22408                 if(k == e.TAB){
22409                     e.stopEvent();
22410                     r = this.doc.selection.createRange();
22411                     if(r){
22412                         r.collapse(true);
22413                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22414                         this.deferFocus();
22415                     }
22416                     return;
22417                 }
22418                 
22419                 if(k == e.ENTER){
22420                     r = this.doc.selection.createRange();
22421                     if(r){
22422                         var target = r.parentElement();
22423                         if(!target || target.tagName.toLowerCase() != 'li'){
22424                             e.stopEvent();
22425                             r.pasteHTML('<br />');
22426                             r.collapse(false);
22427                             r.select();
22428                         }
22429                     }
22430                 }
22431                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22432                     this.cleanUpPaste.defer(100, this);
22433                     return;
22434                 }
22435                 
22436                 
22437             };
22438         }else if(Roo.isOpera){
22439             return function(e){
22440                 var k = e.getKey();
22441                 if(k == e.TAB){
22442                     e.stopEvent();
22443                     this.win.focus();
22444                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22445                     this.deferFocus();
22446                 }
22447                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22448                     this.cleanUpPaste.defer(100, this);
22449                     return;
22450                 }
22451                 
22452             };
22453         }else if(Roo.isSafari){
22454             return function(e){
22455                 var k = e.getKey();
22456                 
22457                 if(k == e.TAB){
22458                     e.stopEvent();
22459                     this.execCmd('InsertText','\t');
22460                     this.deferFocus();
22461                     return;
22462                 }
22463                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22464                     this.cleanUpPaste.defer(100, this);
22465                     return;
22466                 }
22467                 
22468              };
22469         }
22470     }(),
22471     
22472     getAllAncestors: function()
22473     {
22474         var p = this.getSelectedNode();
22475         var a = [];
22476         if (!p) {
22477             a.push(p); // push blank onto stack..
22478             p = this.getParentElement();
22479         }
22480         
22481         
22482         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22483             a.push(p);
22484             p = p.parentNode;
22485         }
22486         a.push(this.doc.body);
22487         return a;
22488     },
22489     lastSel : false,
22490     lastSelNode : false,
22491     
22492     
22493     getSelection : function() 
22494     {
22495         this.assignDocWin();
22496         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22497     },
22498     
22499     getSelectedNode: function() 
22500     {
22501         // this may only work on Gecko!!!
22502         
22503         // should we cache this!!!!
22504         
22505         
22506         
22507          
22508         var range = this.createRange(this.getSelection()).cloneRange();
22509         
22510         if (Roo.isIE) {
22511             var parent = range.parentElement();
22512             while (true) {
22513                 var testRange = range.duplicate();
22514                 testRange.moveToElementText(parent);
22515                 if (testRange.inRange(range)) {
22516                     break;
22517                 }
22518                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22519                     break;
22520                 }
22521                 parent = parent.parentElement;
22522             }
22523             return parent;
22524         }
22525         
22526         // is ancestor a text element.
22527         var ac =  range.commonAncestorContainer;
22528         if (ac.nodeType == 3) {
22529             ac = ac.parentNode;
22530         }
22531         
22532         var ar = ac.childNodes;
22533          
22534         var nodes = [];
22535         var other_nodes = [];
22536         var has_other_nodes = false;
22537         for (var i=0;i<ar.length;i++) {
22538             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22539                 continue;
22540             }
22541             // fullly contained node.
22542             
22543             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22544                 nodes.push(ar[i]);
22545                 continue;
22546             }
22547             
22548             // probably selected..
22549             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22550                 other_nodes.push(ar[i]);
22551                 continue;
22552             }
22553             // outer..
22554             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22555                 continue;
22556             }
22557             
22558             
22559             has_other_nodes = true;
22560         }
22561         if (!nodes.length && other_nodes.length) {
22562             nodes= other_nodes;
22563         }
22564         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22565             return false;
22566         }
22567         
22568         return nodes[0];
22569     },
22570     createRange: function(sel)
22571     {
22572         // this has strange effects when using with 
22573         // top toolbar - not sure if it's a great idea.
22574         //this.editor.contentWindow.focus();
22575         if (typeof sel != "undefined") {
22576             try {
22577                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22578             } catch(e) {
22579                 return this.doc.createRange();
22580             }
22581         } else {
22582             return this.doc.createRange();
22583         }
22584     },
22585     getParentElement: function()
22586     {
22587         
22588         this.assignDocWin();
22589         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22590         
22591         var range = this.createRange(sel);
22592          
22593         try {
22594             var p = range.commonAncestorContainer;
22595             while (p.nodeType == 3) { // text node
22596                 p = p.parentNode;
22597             }
22598             return p;
22599         } catch (e) {
22600             return null;
22601         }
22602     
22603     },
22604     /***
22605      *
22606      * Range intersection.. the hard stuff...
22607      *  '-1' = before
22608      *  '0' = hits..
22609      *  '1' = after.
22610      *         [ -- selected range --- ]
22611      *   [fail]                        [fail]
22612      *
22613      *    basically..
22614      *      if end is before start or  hits it. fail.
22615      *      if start is after end or hits it fail.
22616      *
22617      *   if either hits (but other is outside. - then it's not 
22618      *   
22619      *    
22620      **/
22621     
22622     
22623     // @see http://www.thismuchiknow.co.uk/?p=64.
22624     rangeIntersectsNode : function(range, node)
22625     {
22626         var nodeRange = node.ownerDocument.createRange();
22627         try {
22628             nodeRange.selectNode(node);
22629         } catch (e) {
22630             nodeRange.selectNodeContents(node);
22631         }
22632     
22633         var rangeStartRange = range.cloneRange();
22634         rangeStartRange.collapse(true);
22635     
22636         var rangeEndRange = range.cloneRange();
22637         rangeEndRange.collapse(false);
22638     
22639         var nodeStartRange = nodeRange.cloneRange();
22640         nodeStartRange.collapse(true);
22641     
22642         var nodeEndRange = nodeRange.cloneRange();
22643         nodeEndRange.collapse(false);
22644     
22645         return rangeStartRange.compareBoundaryPoints(
22646                  Range.START_TO_START, nodeEndRange) == -1 &&
22647                rangeEndRange.compareBoundaryPoints(
22648                  Range.START_TO_START, nodeStartRange) == 1;
22649         
22650          
22651     },
22652     rangeCompareNode : function(range, node)
22653     {
22654         var nodeRange = node.ownerDocument.createRange();
22655         try {
22656             nodeRange.selectNode(node);
22657         } catch (e) {
22658             nodeRange.selectNodeContents(node);
22659         }
22660         
22661         
22662         range.collapse(true);
22663     
22664         nodeRange.collapse(true);
22665      
22666         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22667         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22668          
22669         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22670         
22671         var nodeIsBefore   =  ss == 1;
22672         var nodeIsAfter    = ee == -1;
22673         
22674         if (nodeIsBefore && nodeIsAfter) {
22675             return 0; // outer
22676         }
22677         if (!nodeIsBefore && nodeIsAfter) {
22678             return 1; //right trailed.
22679         }
22680         
22681         if (nodeIsBefore && !nodeIsAfter) {
22682             return 2;  // left trailed.
22683         }
22684         // fully contined.
22685         return 3;
22686     },
22687
22688     // private? - in a new class?
22689     cleanUpPaste :  function()
22690     {
22691         // cleans up the whole document..
22692         Roo.log('cleanuppaste');
22693         
22694         this.cleanUpChildren(this.doc.body);
22695         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22696         if (clean != this.doc.body.innerHTML) {
22697             this.doc.body.innerHTML = clean;
22698         }
22699         
22700     },
22701     
22702     cleanWordChars : function(input) {// change the chars to hex code
22703         var he = Roo.HtmlEditorCore;
22704         
22705         var output = input;
22706         Roo.each(he.swapCodes, function(sw) { 
22707             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22708             
22709             output = output.replace(swapper, sw[1]);
22710         });
22711         
22712         return output;
22713     },
22714     
22715     
22716     cleanUpChildren : function (n)
22717     {
22718         if (!n.childNodes.length) {
22719             return;
22720         }
22721         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22722            this.cleanUpChild(n.childNodes[i]);
22723         }
22724     },
22725     
22726     
22727         
22728     
22729     cleanUpChild : function (node)
22730     {
22731         var ed = this;
22732         //console.log(node);
22733         if (node.nodeName == "#text") {
22734             // clean up silly Windows -- stuff?
22735             return; 
22736         }
22737         if (node.nodeName == "#comment") {
22738             node.parentNode.removeChild(node);
22739             // clean up silly Windows -- stuff?
22740             return; 
22741         }
22742         var lcname = node.tagName.toLowerCase();
22743         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22744         // whitelist of tags..
22745         
22746         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22747             // remove node.
22748             node.parentNode.removeChild(node);
22749             return;
22750             
22751         }
22752         
22753         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22754         
22755         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22756         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22757         
22758         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22759         //    remove_keep_children = true;
22760         //}
22761         
22762         if (remove_keep_children) {
22763             this.cleanUpChildren(node);
22764             // inserts everything just before this node...
22765             while (node.childNodes.length) {
22766                 var cn = node.childNodes[0];
22767                 node.removeChild(cn);
22768                 node.parentNode.insertBefore(cn, node);
22769             }
22770             node.parentNode.removeChild(node);
22771             return;
22772         }
22773         
22774         if (!node.attributes || !node.attributes.length) {
22775             this.cleanUpChildren(node);
22776             return;
22777         }
22778         
22779         function cleanAttr(n,v)
22780         {
22781             
22782             if (v.match(/^\./) || v.match(/^\//)) {
22783                 return;
22784             }
22785             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22786                 return;
22787             }
22788             if (v.match(/^#/)) {
22789                 return;
22790             }
22791 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22792             node.removeAttribute(n);
22793             
22794         }
22795         
22796         var cwhite = this.cwhite;
22797         var cblack = this.cblack;
22798             
22799         function cleanStyle(n,v)
22800         {
22801             if (v.match(/expression/)) { //XSS?? should we even bother..
22802                 node.removeAttribute(n);
22803                 return;
22804             }
22805             
22806             var parts = v.split(/;/);
22807             var clean = [];
22808             
22809             Roo.each(parts, function(p) {
22810                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22811                 if (!p.length) {
22812                     return true;
22813                 }
22814                 var l = p.split(':').shift().replace(/\s+/g,'');
22815                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22816                 
22817                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22818 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22819                     //node.removeAttribute(n);
22820                     return true;
22821                 }
22822                 //Roo.log()
22823                 // only allow 'c whitelisted system attributes'
22824                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22825 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22826                     //node.removeAttribute(n);
22827                     return true;
22828                 }
22829                 
22830                 
22831                  
22832                 
22833                 clean.push(p);
22834                 return true;
22835             });
22836             if (clean.length) { 
22837                 node.setAttribute(n, clean.join(';'));
22838             } else {
22839                 node.removeAttribute(n);
22840             }
22841             
22842         }
22843         
22844         
22845         for (var i = node.attributes.length-1; i > -1 ; i--) {
22846             var a = node.attributes[i];
22847             //console.log(a);
22848             
22849             if (a.name.toLowerCase().substr(0,2)=='on')  {
22850                 node.removeAttribute(a.name);
22851                 continue;
22852             }
22853             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22854                 node.removeAttribute(a.name);
22855                 continue;
22856             }
22857             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22858                 cleanAttr(a.name,a.value); // fixme..
22859                 continue;
22860             }
22861             if (a.name == 'style') {
22862                 cleanStyle(a.name,a.value);
22863                 continue;
22864             }
22865             /// clean up MS crap..
22866             // tecnically this should be a list of valid class'es..
22867             
22868             
22869             if (a.name == 'class') {
22870                 if (a.value.match(/^Mso/)) {
22871                     node.className = '';
22872                 }
22873                 
22874                 if (a.value.match(/^body$/)) {
22875                     node.className = '';
22876                 }
22877                 continue;
22878             }
22879             
22880             // style cleanup!?
22881             // class cleanup?
22882             
22883         }
22884         
22885         
22886         this.cleanUpChildren(node);
22887         
22888         
22889     },
22890     
22891     /**
22892      * Clean up MS wordisms...
22893      */
22894     cleanWord : function(node)
22895     {
22896         
22897         
22898         if (!node) {
22899             this.cleanWord(this.doc.body);
22900             return;
22901         }
22902         if (node.nodeName == "#text") {
22903             // clean up silly Windows -- stuff?
22904             return; 
22905         }
22906         if (node.nodeName == "#comment") {
22907             node.parentNode.removeChild(node);
22908             // clean up silly Windows -- stuff?
22909             return; 
22910         }
22911         
22912         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22913             node.parentNode.removeChild(node);
22914             return;
22915         }
22916         
22917         // remove - but keep children..
22918         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22919             while (node.childNodes.length) {
22920                 var cn = node.childNodes[0];
22921                 node.removeChild(cn);
22922                 node.parentNode.insertBefore(cn, node);
22923             }
22924             node.parentNode.removeChild(node);
22925             this.iterateChildren(node, this.cleanWord);
22926             return;
22927         }
22928         // clean styles
22929         if (node.className.length) {
22930             
22931             var cn = node.className.split(/\W+/);
22932             var cna = [];
22933             Roo.each(cn, function(cls) {
22934                 if (cls.match(/Mso[a-zA-Z]+/)) {
22935                     return;
22936                 }
22937                 cna.push(cls);
22938             });
22939             node.className = cna.length ? cna.join(' ') : '';
22940             if (!cna.length) {
22941                 node.removeAttribute("class");
22942             }
22943         }
22944         
22945         if (node.hasAttribute("lang")) {
22946             node.removeAttribute("lang");
22947         }
22948         
22949         if (node.hasAttribute("style")) {
22950             
22951             var styles = node.getAttribute("style").split(";");
22952             var nstyle = [];
22953             Roo.each(styles, function(s) {
22954                 if (!s.match(/:/)) {
22955                     return;
22956                 }
22957                 var kv = s.split(":");
22958                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22959                     return;
22960                 }
22961                 // what ever is left... we allow.
22962                 nstyle.push(s);
22963             });
22964             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22965             if (!nstyle.length) {
22966                 node.removeAttribute('style');
22967             }
22968         }
22969         this.iterateChildren(node, this.cleanWord);
22970         
22971         
22972         
22973     },
22974     /**
22975      * iterateChildren of a Node, calling fn each time, using this as the scole..
22976      * @param {DomNode} node node to iterate children of.
22977      * @param {Function} fn method of this class to call on each item.
22978      */
22979     iterateChildren : function(node, fn)
22980     {
22981         if (!node.childNodes.length) {
22982                 return;
22983         }
22984         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22985            fn.call(this, node.childNodes[i])
22986         }
22987     },
22988     
22989     
22990     /**
22991      * cleanTableWidths.
22992      *
22993      * Quite often pasting from word etc.. results in tables with column and widths.
22994      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22995      *
22996      */
22997     cleanTableWidths : function(node)
22998     {
22999          
23000          
23001         if (!node) {
23002             this.cleanTableWidths(this.doc.body);
23003             return;
23004         }
23005         
23006         // ignore list...
23007         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23008             return; 
23009         }
23010         Roo.log(node.tagName);
23011         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23012             this.iterateChildren(node, this.cleanTableWidths);
23013             return;
23014         }
23015         if (node.hasAttribute('width')) {
23016             node.removeAttribute('width');
23017         }
23018         
23019          
23020         if (node.hasAttribute("style")) {
23021             // pretty basic...
23022             
23023             var styles = node.getAttribute("style").split(";");
23024             var nstyle = [];
23025             Roo.each(styles, function(s) {
23026                 if (!s.match(/:/)) {
23027                     return;
23028                 }
23029                 var kv = s.split(":");
23030                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23031                     return;
23032                 }
23033                 // what ever is left... we allow.
23034                 nstyle.push(s);
23035             });
23036             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23037             if (!nstyle.length) {
23038                 node.removeAttribute('style');
23039             }
23040         }
23041         
23042         this.iterateChildren(node, this.cleanTableWidths);
23043         
23044         
23045     },
23046     
23047     
23048     
23049     
23050     domToHTML : function(currentElement, depth, nopadtext) {
23051         
23052         depth = depth || 0;
23053         nopadtext = nopadtext || false;
23054     
23055         if (!currentElement) {
23056             return this.domToHTML(this.doc.body);
23057         }
23058         
23059         //Roo.log(currentElement);
23060         var j;
23061         var allText = false;
23062         var nodeName = currentElement.nodeName;
23063         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23064         
23065         if  (nodeName == '#text') {
23066             
23067             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23068         }
23069         
23070         
23071         var ret = '';
23072         if (nodeName != 'BODY') {
23073              
23074             var i = 0;
23075             // Prints the node tagName, such as <A>, <IMG>, etc
23076             if (tagName) {
23077                 var attr = [];
23078                 for(i = 0; i < currentElement.attributes.length;i++) {
23079                     // quoting?
23080                     var aname = currentElement.attributes.item(i).name;
23081                     if (!currentElement.attributes.item(i).value.length) {
23082                         continue;
23083                     }
23084                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23085                 }
23086                 
23087                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23088             } 
23089             else {
23090                 
23091                 // eack
23092             }
23093         } else {
23094             tagName = false;
23095         }
23096         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23097             return ret;
23098         }
23099         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23100             nopadtext = true;
23101         }
23102         
23103         
23104         // Traverse the tree
23105         i = 0;
23106         var currentElementChild = currentElement.childNodes.item(i);
23107         var allText = true;
23108         var innerHTML  = '';
23109         lastnode = '';
23110         while (currentElementChild) {
23111             // Formatting code (indent the tree so it looks nice on the screen)
23112             var nopad = nopadtext;
23113             if (lastnode == 'SPAN') {
23114                 nopad  = true;
23115             }
23116             // text
23117             if  (currentElementChild.nodeName == '#text') {
23118                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23119                 toadd = nopadtext ? toadd : toadd.trim();
23120                 if (!nopad && toadd.length > 80) {
23121                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23122                 }
23123                 innerHTML  += toadd;
23124                 
23125                 i++;
23126                 currentElementChild = currentElement.childNodes.item(i);
23127                 lastNode = '';
23128                 continue;
23129             }
23130             allText = false;
23131             
23132             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23133                 
23134             // Recursively traverse the tree structure of the child node
23135             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23136             lastnode = currentElementChild.nodeName;
23137             i++;
23138             currentElementChild=currentElement.childNodes.item(i);
23139         }
23140         
23141         ret += innerHTML;
23142         
23143         if (!allText) {
23144                 // The remaining code is mostly for formatting the tree
23145             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23146         }
23147         
23148         
23149         if (tagName) {
23150             ret+= "</"+tagName+">";
23151         }
23152         return ret;
23153         
23154     },
23155         
23156     applyBlacklists : function()
23157     {
23158         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23159         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23160         
23161         this.white = [];
23162         this.black = [];
23163         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23164             if (b.indexOf(tag) > -1) {
23165                 return;
23166             }
23167             this.white.push(tag);
23168             
23169         }, this);
23170         
23171         Roo.each(w, function(tag) {
23172             if (b.indexOf(tag) > -1) {
23173                 return;
23174             }
23175             if (this.white.indexOf(tag) > -1) {
23176                 return;
23177             }
23178             this.white.push(tag);
23179             
23180         }, this);
23181         
23182         
23183         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23184             if (w.indexOf(tag) > -1) {
23185                 return;
23186             }
23187             this.black.push(tag);
23188             
23189         }, this);
23190         
23191         Roo.each(b, function(tag) {
23192             if (w.indexOf(tag) > -1) {
23193                 return;
23194             }
23195             if (this.black.indexOf(tag) > -1) {
23196                 return;
23197             }
23198             this.black.push(tag);
23199             
23200         }, this);
23201         
23202         
23203         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23204         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23205         
23206         this.cwhite = [];
23207         this.cblack = [];
23208         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23209             if (b.indexOf(tag) > -1) {
23210                 return;
23211             }
23212             this.cwhite.push(tag);
23213             
23214         }, this);
23215         
23216         Roo.each(w, function(tag) {
23217             if (b.indexOf(tag) > -1) {
23218                 return;
23219             }
23220             if (this.cwhite.indexOf(tag) > -1) {
23221                 return;
23222             }
23223             this.cwhite.push(tag);
23224             
23225         }, this);
23226         
23227         
23228         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23229             if (w.indexOf(tag) > -1) {
23230                 return;
23231             }
23232             this.cblack.push(tag);
23233             
23234         }, this);
23235         
23236         Roo.each(b, function(tag) {
23237             if (w.indexOf(tag) > -1) {
23238                 return;
23239             }
23240             if (this.cblack.indexOf(tag) > -1) {
23241                 return;
23242             }
23243             this.cblack.push(tag);
23244             
23245         }, this);
23246     },
23247     
23248     setStylesheets : function(stylesheets)
23249     {
23250         if(typeof(stylesheets) == 'string'){
23251             Roo.get(this.iframe.contentDocument.head).createChild({
23252                 tag : 'link',
23253                 rel : 'stylesheet',
23254                 type : 'text/css',
23255                 href : stylesheets
23256             });
23257             
23258             return;
23259         }
23260         var _this = this;
23261      
23262         Roo.each(stylesheets, function(s) {
23263             if(!s.length){
23264                 return;
23265             }
23266             
23267             Roo.get(_this.iframe.contentDocument.head).createChild({
23268                 tag : 'link',
23269                 rel : 'stylesheet',
23270                 type : 'text/css',
23271                 href : s
23272             });
23273         });
23274
23275         
23276     },
23277     
23278     removeStylesheets : function()
23279     {
23280         var _this = this;
23281         
23282         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23283             s.remove();
23284         });
23285     },
23286     
23287     setStyle : function(style)
23288     {
23289         Roo.get(this.iframe.contentDocument.head).createChild({
23290             tag : 'style',
23291             type : 'text/css',
23292             html : style
23293         });
23294
23295         return;
23296     }
23297     
23298     // hide stuff that is not compatible
23299     /**
23300      * @event blur
23301      * @hide
23302      */
23303     /**
23304      * @event change
23305      * @hide
23306      */
23307     /**
23308      * @event focus
23309      * @hide
23310      */
23311     /**
23312      * @event specialkey
23313      * @hide
23314      */
23315     /**
23316      * @cfg {String} fieldClass @hide
23317      */
23318     /**
23319      * @cfg {String} focusClass @hide
23320      */
23321     /**
23322      * @cfg {String} autoCreate @hide
23323      */
23324     /**
23325      * @cfg {String} inputType @hide
23326      */
23327     /**
23328      * @cfg {String} invalidClass @hide
23329      */
23330     /**
23331      * @cfg {String} invalidText @hide
23332      */
23333     /**
23334      * @cfg {String} msgFx @hide
23335      */
23336     /**
23337      * @cfg {String} validateOnBlur @hide
23338      */
23339 });
23340
23341 Roo.HtmlEditorCore.white = [
23342         'area', 'br', 'img', 'input', 'hr', 'wbr',
23343         
23344        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23345        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23346        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23347        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23348        'table',   'ul',         'xmp', 
23349        
23350        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23351       'thead',   'tr', 
23352      
23353       'dir', 'menu', 'ol', 'ul', 'dl',
23354        
23355       'embed',  'object'
23356 ];
23357
23358
23359 Roo.HtmlEditorCore.black = [
23360     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23361         'applet', // 
23362         'base',   'basefont', 'bgsound', 'blink',  'body', 
23363         'frame',  'frameset', 'head',    'html',   'ilayer', 
23364         'iframe', 'layer',  'link',     'meta',    'object',   
23365         'script', 'style' ,'title',  'xml' // clean later..
23366 ];
23367 Roo.HtmlEditorCore.clean = [
23368     'script', 'style', 'title', 'xml'
23369 ];
23370 Roo.HtmlEditorCore.remove = [
23371     'font'
23372 ];
23373 // attributes..
23374
23375 Roo.HtmlEditorCore.ablack = [
23376     'on'
23377 ];
23378     
23379 Roo.HtmlEditorCore.aclean = [ 
23380     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23381 ];
23382
23383 // protocols..
23384 Roo.HtmlEditorCore.pwhite= [
23385         'http',  'https',  'mailto'
23386 ];
23387
23388 // white listed style attributes.
23389 Roo.HtmlEditorCore.cwhite= [
23390       //  'text-align', /// default is to allow most things..
23391       
23392          
23393 //        'font-size'//??
23394 ];
23395
23396 // black listed style attributes.
23397 Roo.HtmlEditorCore.cblack= [
23398       //  'font-size' -- this can be set by the project 
23399 ];
23400
23401
23402 Roo.HtmlEditorCore.swapCodes   =[ 
23403     [    8211, "--" ], 
23404     [    8212, "--" ], 
23405     [    8216,  "'" ],  
23406     [    8217, "'" ],  
23407     [    8220, '"' ],  
23408     [    8221, '"' ],  
23409     [    8226, "*" ],  
23410     [    8230, "..." ]
23411 ]; 
23412
23413     /*
23414  * - LGPL
23415  *
23416  * HtmlEditor
23417  * 
23418  */
23419
23420 /**
23421  * @class Roo.bootstrap.HtmlEditor
23422  * @extends Roo.bootstrap.TextArea
23423  * Bootstrap HtmlEditor class
23424
23425  * @constructor
23426  * Create a new HtmlEditor
23427  * @param {Object} config The config object
23428  */
23429
23430 Roo.bootstrap.HtmlEditor = function(config){
23431     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23432     if (!this.toolbars) {
23433         this.toolbars = [];
23434     }
23435     
23436     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23437     this.addEvents({
23438             /**
23439              * @event initialize
23440              * Fires when the editor is fully initialized (including the iframe)
23441              * @param {HtmlEditor} this
23442              */
23443             initialize: true,
23444             /**
23445              * @event activate
23446              * Fires when the editor is first receives the focus. Any insertion must wait
23447              * until after this event.
23448              * @param {HtmlEditor} this
23449              */
23450             activate: true,
23451              /**
23452              * @event beforesync
23453              * Fires before the textarea is updated with content from the editor iframe. Return false
23454              * to cancel the sync.
23455              * @param {HtmlEditor} this
23456              * @param {String} html
23457              */
23458             beforesync: true,
23459              /**
23460              * @event beforepush
23461              * Fires before the iframe editor is updated with content from the textarea. Return false
23462              * to cancel the push.
23463              * @param {HtmlEditor} this
23464              * @param {String} html
23465              */
23466             beforepush: true,
23467              /**
23468              * @event sync
23469              * Fires when the textarea is updated with content from the editor iframe.
23470              * @param {HtmlEditor} this
23471              * @param {String} html
23472              */
23473             sync: true,
23474              /**
23475              * @event push
23476              * Fires when the iframe editor is updated with content from the textarea.
23477              * @param {HtmlEditor} this
23478              * @param {String} html
23479              */
23480             push: true,
23481              /**
23482              * @event editmodechange
23483              * Fires when the editor switches edit modes
23484              * @param {HtmlEditor} this
23485              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23486              */
23487             editmodechange: true,
23488             /**
23489              * @event editorevent
23490              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23491              * @param {HtmlEditor} this
23492              */
23493             editorevent: true,
23494             /**
23495              * @event firstfocus
23496              * Fires when on first focus - needed by toolbars..
23497              * @param {HtmlEditor} this
23498              */
23499             firstfocus: true,
23500             /**
23501              * @event autosave
23502              * Auto save the htmlEditor value as a file into Events
23503              * @param {HtmlEditor} this
23504              */
23505             autosave: true,
23506             /**
23507              * @event savedpreview
23508              * preview the saved version of htmlEditor
23509              * @param {HtmlEditor} this
23510              */
23511             savedpreview: true
23512         });
23513 };
23514
23515
23516 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23517     
23518     
23519       /**
23520      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23521      */
23522     toolbars : false,
23523     
23524      /**
23525     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23526     */
23527     btns : [],
23528    
23529      /**
23530      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23531      *                        Roo.resizable.
23532      */
23533     resizable : false,
23534      /**
23535      * @cfg {Number} height (in pixels)
23536      */   
23537     height: 300,
23538    /**
23539      * @cfg {Number} width (in pixels)
23540      */   
23541     width: false,
23542     
23543     /**
23544      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23545      * 
23546      */
23547     stylesheets: false,
23548     
23549     // id of frame..
23550     frameId: false,
23551     
23552     // private properties
23553     validationEvent : false,
23554     deferHeight: true,
23555     initialized : false,
23556     activated : false,
23557     
23558     onFocus : Roo.emptyFn,
23559     iframePad:3,
23560     hideMode:'offsets',
23561     
23562     tbContainer : false,
23563     
23564     bodyCls : '',
23565     
23566     toolbarContainer :function() {
23567         return this.wrap.select('.x-html-editor-tb',true).first();
23568     },
23569
23570     /**
23571      * Protected method that will not generally be called directly. It
23572      * is called when the editor creates its toolbar. Override this method if you need to
23573      * add custom toolbar buttons.
23574      * @param {HtmlEditor} editor
23575      */
23576     createToolbar : function(){
23577         Roo.log('renewing');
23578         Roo.log("create toolbars");
23579         
23580         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23581         this.toolbars[0].render(this.toolbarContainer());
23582         
23583         return;
23584         
23585 //        if (!editor.toolbars || !editor.toolbars.length) {
23586 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23587 //        }
23588 //        
23589 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23590 //            editor.toolbars[i] = Roo.factory(
23591 //                    typeof(editor.toolbars[i]) == 'string' ?
23592 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23593 //                Roo.bootstrap.HtmlEditor);
23594 //            editor.toolbars[i].init(editor);
23595 //        }
23596     },
23597
23598      
23599     // private
23600     onRender : function(ct, position)
23601     {
23602        // Roo.log("Call onRender: " + this.xtype);
23603         var _t = this;
23604         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23605       
23606         this.wrap = this.inputEl().wrap({
23607             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23608         });
23609         
23610         this.editorcore.onRender(ct, position);
23611          
23612         if (this.resizable) {
23613             this.resizeEl = new Roo.Resizable(this.wrap, {
23614                 pinned : true,
23615                 wrap: true,
23616                 dynamic : true,
23617                 minHeight : this.height,
23618                 height: this.height,
23619                 handles : this.resizable,
23620                 width: this.width,
23621                 listeners : {
23622                     resize : function(r, w, h) {
23623                         _t.onResize(w,h); // -something
23624                     }
23625                 }
23626             });
23627             
23628         }
23629         this.createToolbar(this);
23630        
23631         
23632         if(!this.width && this.resizable){
23633             this.setSize(this.wrap.getSize());
23634         }
23635         if (this.resizeEl) {
23636             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23637             // should trigger onReize..
23638         }
23639         
23640     },
23641
23642     // private
23643     onResize : function(w, h)
23644     {
23645         Roo.log('resize: ' +w + ',' + h );
23646         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23647         var ew = false;
23648         var eh = false;
23649         
23650         if(this.inputEl() ){
23651             if(typeof w == 'number'){
23652                 var aw = w - this.wrap.getFrameWidth('lr');
23653                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23654                 ew = aw;
23655             }
23656             if(typeof h == 'number'){
23657                  var tbh = -11;  // fixme it needs to tool bar size!
23658                 for (var i =0; i < this.toolbars.length;i++) {
23659                     // fixme - ask toolbars for heights?
23660                     tbh += this.toolbars[i].el.getHeight();
23661                     //if (this.toolbars[i].footer) {
23662                     //    tbh += this.toolbars[i].footer.el.getHeight();
23663                     //}
23664                 }
23665               
23666                 
23667                 
23668                 
23669                 
23670                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23671                 ah -= 5; // knock a few pixes off for look..
23672                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23673                 var eh = ah;
23674             }
23675         }
23676         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23677         this.editorcore.onResize(ew,eh);
23678         
23679     },
23680
23681     /**
23682      * Toggles the editor between standard and source edit mode.
23683      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23684      */
23685     toggleSourceEdit : function(sourceEditMode)
23686     {
23687         this.editorcore.toggleSourceEdit(sourceEditMode);
23688         
23689         if(this.editorcore.sourceEditMode){
23690             Roo.log('editor - showing textarea');
23691             
23692 //            Roo.log('in');
23693 //            Roo.log(this.syncValue());
23694             this.syncValue();
23695             this.inputEl().removeClass(['hide', 'x-hidden']);
23696             this.inputEl().dom.removeAttribute('tabIndex');
23697             this.inputEl().focus();
23698         }else{
23699             Roo.log('editor - hiding textarea');
23700 //            Roo.log('out')
23701 //            Roo.log(this.pushValue()); 
23702             this.pushValue();
23703             
23704             this.inputEl().addClass(['hide', 'x-hidden']);
23705             this.inputEl().dom.setAttribute('tabIndex', -1);
23706             //this.deferFocus();
23707         }
23708          
23709         if(this.resizable){
23710             this.setSize(this.wrap.getSize());
23711         }
23712         
23713         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23714     },
23715  
23716     // private (for BoxComponent)
23717     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23718
23719     // private (for BoxComponent)
23720     getResizeEl : function(){
23721         return this.wrap;
23722     },
23723
23724     // private (for BoxComponent)
23725     getPositionEl : function(){
23726         return this.wrap;
23727     },
23728
23729     // private
23730     initEvents : function(){
23731         this.originalValue = this.getValue();
23732     },
23733
23734 //    /**
23735 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23736 //     * @method
23737 //     */
23738 //    markInvalid : Roo.emptyFn,
23739 //    /**
23740 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23741 //     * @method
23742 //     */
23743 //    clearInvalid : Roo.emptyFn,
23744
23745     setValue : function(v){
23746         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23747         this.editorcore.pushValue();
23748     },
23749
23750      
23751     // private
23752     deferFocus : function(){
23753         this.focus.defer(10, this);
23754     },
23755
23756     // doc'ed in Field
23757     focus : function(){
23758         this.editorcore.focus();
23759         
23760     },
23761       
23762
23763     // private
23764     onDestroy : function(){
23765         
23766         
23767         
23768         if(this.rendered){
23769             
23770             for (var i =0; i < this.toolbars.length;i++) {
23771                 // fixme - ask toolbars for heights?
23772                 this.toolbars[i].onDestroy();
23773             }
23774             
23775             this.wrap.dom.innerHTML = '';
23776             this.wrap.remove();
23777         }
23778     },
23779
23780     // private
23781     onFirstFocus : function(){
23782         //Roo.log("onFirstFocus");
23783         this.editorcore.onFirstFocus();
23784          for (var i =0; i < this.toolbars.length;i++) {
23785             this.toolbars[i].onFirstFocus();
23786         }
23787         
23788     },
23789     
23790     // private
23791     syncValue : function()
23792     {   
23793         this.editorcore.syncValue();
23794     },
23795     
23796     pushValue : function()
23797     {   
23798         this.editorcore.pushValue();
23799     }
23800      
23801     
23802     // hide stuff that is not compatible
23803     /**
23804      * @event blur
23805      * @hide
23806      */
23807     /**
23808      * @event change
23809      * @hide
23810      */
23811     /**
23812      * @event focus
23813      * @hide
23814      */
23815     /**
23816      * @event specialkey
23817      * @hide
23818      */
23819     /**
23820      * @cfg {String} fieldClass @hide
23821      */
23822     /**
23823      * @cfg {String} focusClass @hide
23824      */
23825     /**
23826      * @cfg {String} autoCreate @hide
23827      */
23828     /**
23829      * @cfg {String} inputType @hide
23830      */
23831     /**
23832      * @cfg {String} invalidClass @hide
23833      */
23834     /**
23835      * @cfg {String} invalidText @hide
23836      */
23837     /**
23838      * @cfg {String} msgFx @hide
23839      */
23840     /**
23841      * @cfg {String} validateOnBlur @hide
23842      */
23843 });
23844  
23845     
23846    
23847    
23848    
23849       
23850 Roo.namespace('Roo.bootstrap.htmleditor');
23851 /**
23852  * @class Roo.bootstrap.HtmlEditorToolbar1
23853  * Basic Toolbar
23854  * 
23855  * Usage:
23856  *
23857  new Roo.bootstrap.HtmlEditor({
23858     ....
23859     toolbars : [
23860         new Roo.bootstrap.HtmlEditorToolbar1({
23861             disable : { fonts: 1 , format: 1, ..., ... , ...],
23862             btns : [ .... ]
23863         })
23864     }
23865      
23866  * 
23867  * @cfg {Object} disable List of elements to disable..
23868  * @cfg {Array} btns List of additional buttons.
23869  * 
23870  * 
23871  * NEEDS Extra CSS? 
23872  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23873  */
23874  
23875 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23876 {
23877     
23878     Roo.apply(this, config);
23879     
23880     // default disabled, based on 'good practice'..
23881     this.disable = this.disable || {};
23882     Roo.applyIf(this.disable, {
23883         fontSize : true,
23884         colors : true,
23885         specialElements : true
23886     });
23887     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23888     
23889     this.editor = config.editor;
23890     this.editorcore = config.editor.editorcore;
23891     
23892     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23893     
23894     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23895     // dont call parent... till later.
23896 }
23897 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23898      
23899     bar : true,
23900     
23901     editor : false,
23902     editorcore : false,
23903     
23904     
23905     formats : [
23906         "p" ,  
23907         "h1","h2","h3","h4","h5","h6", 
23908         "pre", "code", 
23909         "abbr", "acronym", "address", "cite", "samp", "var",
23910         'div','span'
23911     ],
23912     
23913     onRender : function(ct, position)
23914     {
23915        // Roo.log("Call onRender: " + this.xtype);
23916         
23917        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23918        Roo.log(this.el);
23919        this.el.dom.style.marginBottom = '0';
23920        var _this = this;
23921        var editorcore = this.editorcore;
23922        var editor= this.editor;
23923        
23924        var children = [];
23925        var btn = function(id,cmd , toggle, handler, html){
23926        
23927             var  event = toggle ? 'toggle' : 'click';
23928        
23929             var a = {
23930                 size : 'sm',
23931                 xtype: 'Button',
23932                 xns: Roo.bootstrap,
23933                 glyphicon : id,
23934                 cmd : id || cmd,
23935                 enableToggle:toggle !== false,
23936                 html : html || '',
23937                 pressed : toggle ? false : null,
23938                 listeners : {}
23939             };
23940             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23941                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23942             };
23943             children.push(a);
23944             return a;
23945        }
23946        
23947     //    var cb_box = function...
23948         
23949         var style = {
23950                 xtype: 'Button',
23951                 size : 'sm',
23952                 xns: Roo.bootstrap,
23953                 glyphicon : 'font',
23954                 //html : 'submit'
23955                 menu : {
23956                     xtype: 'Menu',
23957                     xns: Roo.bootstrap,
23958                     items:  []
23959                 }
23960         };
23961         Roo.each(this.formats, function(f) {
23962             style.menu.items.push({
23963                 xtype :'MenuItem',
23964                 xns: Roo.bootstrap,
23965                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23966                 tagname : f,
23967                 listeners : {
23968                     click : function()
23969                     {
23970                         editorcore.insertTag(this.tagname);
23971                         editor.focus();
23972                     }
23973                 }
23974                 
23975             });
23976         });
23977         children.push(style);   
23978         
23979         btn('bold',false,true);
23980         btn('italic',false,true);
23981         btn('align-left', 'justifyleft',true);
23982         btn('align-center', 'justifycenter',true);
23983         btn('align-right' , 'justifyright',true);
23984         btn('link', false, false, function(btn) {
23985             //Roo.log("create link?");
23986             var url = prompt(this.createLinkText, this.defaultLinkValue);
23987             if(url && url != 'http:/'+'/'){
23988                 this.editorcore.relayCmd('createlink', url);
23989             }
23990         }),
23991         btn('list','insertunorderedlist',true);
23992         btn('pencil', false,true, function(btn){
23993                 Roo.log(this);
23994                 this.toggleSourceEdit(btn.pressed);
23995         });
23996         
23997         if (this.editor.btns.length > 0) {
23998             for (var i = 0; i<this.editor.btns.length; i++) {
23999                 children.push(this.editor.btns[i]);
24000             }
24001         }
24002         
24003         /*
24004         var cog = {
24005                 xtype: 'Button',
24006                 size : 'sm',
24007                 xns: Roo.bootstrap,
24008                 glyphicon : 'cog',
24009                 //html : 'submit'
24010                 menu : {
24011                     xtype: 'Menu',
24012                     xns: Roo.bootstrap,
24013                     items:  []
24014                 }
24015         };
24016         
24017         cog.menu.items.push({
24018             xtype :'MenuItem',
24019             xns: Roo.bootstrap,
24020             html : Clean styles,
24021             tagname : f,
24022             listeners : {
24023                 click : function()
24024                 {
24025                     editorcore.insertTag(this.tagname);
24026                     editor.focus();
24027                 }
24028             }
24029             
24030         });
24031        */
24032         
24033          
24034        this.xtype = 'NavSimplebar';
24035         
24036         for(var i=0;i< children.length;i++) {
24037             
24038             this.buttons.add(this.addxtypeChild(children[i]));
24039             
24040         }
24041         
24042         editor.on('editorevent', this.updateToolbar, this);
24043     },
24044     onBtnClick : function(id)
24045     {
24046        this.editorcore.relayCmd(id);
24047        this.editorcore.focus();
24048     },
24049     
24050     /**
24051      * Protected method that will not generally be called directly. It triggers
24052      * a toolbar update by reading the markup state of the current selection in the editor.
24053      */
24054     updateToolbar: function(){
24055
24056         if(!this.editorcore.activated){
24057             this.editor.onFirstFocus(); // is this neeed?
24058             return;
24059         }
24060
24061         var btns = this.buttons; 
24062         var doc = this.editorcore.doc;
24063         btns.get('bold').setActive(doc.queryCommandState('bold'));
24064         btns.get('italic').setActive(doc.queryCommandState('italic'));
24065         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24066         
24067         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24068         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24069         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24070         
24071         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24072         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24073          /*
24074         
24075         var ans = this.editorcore.getAllAncestors();
24076         if (this.formatCombo) {
24077             
24078             
24079             var store = this.formatCombo.store;
24080             this.formatCombo.setValue("");
24081             for (var i =0; i < ans.length;i++) {
24082                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24083                     // select it..
24084                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24085                     break;
24086                 }
24087             }
24088         }
24089         
24090         
24091         
24092         // hides menus... - so this cant be on a menu...
24093         Roo.bootstrap.MenuMgr.hideAll();
24094         */
24095         Roo.bootstrap.MenuMgr.hideAll();
24096         //this.editorsyncValue();
24097     },
24098     onFirstFocus: function() {
24099         this.buttons.each(function(item){
24100            item.enable();
24101         });
24102     },
24103     toggleSourceEdit : function(sourceEditMode){
24104         
24105           
24106         if(sourceEditMode){
24107             Roo.log("disabling buttons");
24108            this.buttons.each( function(item){
24109                 if(item.cmd != 'pencil'){
24110                     item.disable();
24111                 }
24112             });
24113           
24114         }else{
24115             Roo.log("enabling buttons");
24116             if(this.editorcore.initialized){
24117                 this.buttons.each( function(item){
24118                     item.enable();
24119                 });
24120             }
24121             
24122         }
24123         Roo.log("calling toggole on editor");
24124         // tell the editor that it's been pressed..
24125         this.editor.toggleSourceEdit(sourceEditMode);
24126        
24127     }
24128 });
24129
24130
24131
24132
24133
24134 /**
24135  * @class Roo.bootstrap.Table.AbstractSelectionModel
24136  * @extends Roo.util.Observable
24137  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24138  * implemented by descendant classes.  This class should not be directly instantiated.
24139  * @constructor
24140  */
24141 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24142     this.locked = false;
24143     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24144 };
24145
24146
24147 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24148     /** @ignore Called by the grid automatically. Do not call directly. */
24149     init : function(grid){
24150         this.grid = grid;
24151         this.initEvents();
24152     },
24153
24154     /**
24155      * Locks the selections.
24156      */
24157     lock : function(){
24158         this.locked = true;
24159     },
24160
24161     /**
24162      * Unlocks the selections.
24163      */
24164     unlock : function(){
24165         this.locked = false;
24166     },
24167
24168     /**
24169      * Returns true if the selections are locked.
24170      * @return {Boolean}
24171      */
24172     isLocked : function(){
24173         return this.locked;
24174     }
24175 });
24176 /**
24177  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24178  * @class Roo.bootstrap.Table.RowSelectionModel
24179  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24180  * It supports multiple selections and keyboard selection/navigation. 
24181  * @constructor
24182  * @param {Object} config
24183  */
24184
24185 Roo.bootstrap.Table.RowSelectionModel = function(config){
24186     Roo.apply(this, config);
24187     this.selections = new Roo.util.MixedCollection(false, function(o){
24188         return o.id;
24189     });
24190
24191     this.last = false;
24192     this.lastActive = false;
24193
24194     this.addEvents({
24195         /**
24196              * @event selectionchange
24197              * Fires when the selection changes
24198              * @param {SelectionModel} this
24199              */
24200             "selectionchange" : true,
24201         /**
24202              * @event afterselectionchange
24203              * Fires after the selection changes (eg. by key press or clicking)
24204              * @param {SelectionModel} this
24205              */
24206             "afterselectionchange" : true,
24207         /**
24208              * @event beforerowselect
24209              * Fires when a row is selected being selected, return false to cancel.
24210              * @param {SelectionModel} this
24211              * @param {Number} rowIndex The selected index
24212              * @param {Boolean} keepExisting False if other selections will be cleared
24213              */
24214             "beforerowselect" : true,
24215         /**
24216              * @event rowselect
24217              * Fires when a row is selected.
24218              * @param {SelectionModel} this
24219              * @param {Number} rowIndex The selected index
24220              * @param {Roo.data.Record} r The record
24221              */
24222             "rowselect" : true,
24223         /**
24224              * @event rowdeselect
24225              * Fires when a row is deselected.
24226              * @param {SelectionModel} this
24227              * @param {Number} rowIndex The selected index
24228              */
24229         "rowdeselect" : true
24230     });
24231     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24232     this.locked = false;
24233  };
24234
24235 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24236     /**
24237      * @cfg {Boolean} singleSelect
24238      * True to allow selection of only one row at a time (defaults to false)
24239      */
24240     singleSelect : false,
24241
24242     // private
24243     initEvents : function()
24244     {
24245
24246         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24247         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24248         //}else{ // allow click to work like normal
24249          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24250         //}
24251         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24252         this.grid.on("rowclick", this.handleMouseDown, this);
24253         
24254         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24255             "up" : function(e){
24256                 if(!e.shiftKey){
24257                     this.selectPrevious(e.shiftKey);
24258                 }else if(this.last !== false && this.lastActive !== false){
24259                     var last = this.last;
24260                     this.selectRange(this.last,  this.lastActive-1);
24261                     this.grid.getView().focusRow(this.lastActive);
24262                     if(last !== false){
24263                         this.last = last;
24264                     }
24265                 }else{
24266                     this.selectFirstRow();
24267                 }
24268                 this.fireEvent("afterselectionchange", this);
24269             },
24270             "down" : function(e){
24271                 if(!e.shiftKey){
24272                     this.selectNext(e.shiftKey);
24273                 }else if(this.last !== false && this.lastActive !== false){
24274                     var last = this.last;
24275                     this.selectRange(this.last,  this.lastActive+1);
24276                     this.grid.getView().focusRow(this.lastActive);
24277                     if(last !== false){
24278                         this.last = last;
24279                     }
24280                 }else{
24281                     this.selectFirstRow();
24282                 }
24283                 this.fireEvent("afterselectionchange", this);
24284             },
24285             scope: this
24286         });
24287         this.grid.store.on('load', function(){
24288             this.selections.clear();
24289         },this);
24290         /*
24291         var view = this.grid.view;
24292         view.on("refresh", this.onRefresh, this);
24293         view.on("rowupdated", this.onRowUpdated, this);
24294         view.on("rowremoved", this.onRemove, this);
24295         */
24296     },
24297
24298     // private
24299     onRefresh : function()
24300     {
24301         var ds = this.grid.store, i, v = this.grid.view;
24302         var s = this.selections;
24303         s.each(function(r){
24304             if((i = ds.indexOfId(r.id)) != -1){
24305                 v.onRowSelect(i);
24306             }else{
24307                 s.remove(r);
24308             }
24309         });
24310     },
24311
24312     // private
24313     onRemove : function(v, index, r){
24314         this.selections.remove(r);
24315     },
24316
24317     // private
24318     onRowUpdated : function(v, index, r){
24319         if(this.isSelected(r)){
24320             v.onRowSelect(index);
24321         }
24322     },
24323
24324     /**
24325      * Select records.
24326      * @param {Array} records The records to select
24327      * @param {Boolean} keepExisting (optional) True to keep existing selections
24328      */
24329     selectRecords : function(records, keepExisting)
24330     {
24331         if(!keepExisting){
24332             this.clearSelections();
24333         }
24334             var ds = this.grid.store;
24335         for(var i = 0, len = records.length; i < len; i++){
24336             this.selectRow(ds.indexOf(records[i]), true);
24337         }
24338     },
24339
24340     /**
24341      * Gets the number of selected rows.
24342      * @return {Number}
24343      */
24344     getCount : function(){
24345         return this.selections.length;
24346     },
24347
24348     /**
24349      * Selects the first row in the grid.
24350      */
24351     selectFirstRow : function(){
24352         this.selectRow(0);
24353     },
24354
24355     /**
24356      * Select the last row.
24357      * @param {Boolean} keepExisting (optional) True to keep existing selections
24358      */
24359     selectLastRow : function(keepExisting){
24360         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24361         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24362     },
24363
24364     /**
24365      * Selects the row immediately following the last selected row.
24366      * @param {Boolean} keepExisting (optional) True to keep existing selections
24367      */
24368     selectNext : function(keepExisting)
24369     {
24370             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24371             this.selectRow(this.last+1, keepExisting);
24372             this.grid.getView().focusRow(this.last);
24373         }
24374     },
24375
24376     /**
24377      * Selects the row that precedes the last selected row.
24378      * @param {Boolean} keepExisting (optional) True to keep existing selections
24379      */
24380     selectPrevious : function(keepExisting){
24381         if(this.last){
24382             this.selectRow(this.last-1, keepExisting);
24383             this.grid.getView().focusRow(this.last);
24384         }
24385     },
24386
24387     /**
24388      * Returns the selected records
24389      * @return {Array} Array of selected records
24390      */
24391     getSelections : function(){
24392         return [].concat(this.selections.items);
24393     },
24394
24395     /**
24396      * Returns the first selected record.
24397      * @return {Record}
24398      */
24399     getSelected : function(){
24400         return this.selections.itemAt(0);
24401     },
24402
24403
24404     /**
24405      * Clears all selections.
24406      */
24407     clearSelections : function(fast)
24408     {
24409         if(this.locked) {
24410             return;
24411         }
24412         if(fast !== true){
24413                 var ds = this.grid.store;
24414             var s = this.selections;
24415             s.each(function(r){
24416                 this.deselectRow(ds.indexOfId(r.id));
24417             }, this);
24418             s.clear();
24419         }else{
24420             this.selections.clear();
24421         }
24422         this.last = false;
24423     },
24424
24425
24426     /**
24427      * Selects all rows.
24428      */
24429     selectAll : function(){
24430         if(this.locked) {
24431             return;
24432         }
24433         this.selections.clear();
24434         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24435             this.selectRow(i, true);
24436         }
24437     },
24438
24439     /**
24440      * Returns True if there is a selection.
24441      * @return {Boolean}
24442      */
24443     hasSelection : function(){
24444         return this.selections.length > 0;
24445     },
24446
24447     /**
24448      * Returns True if the specified row is selected.
24449      * @param {Number/Record} record The record or index of the record to check
24450      * @return {Boolean}
24451      */
24452     isSelected : function(index){
24453             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24454         return (r && this.selections.key(r.id) ? true : false);
24455     },
24456
24457     /**
24458      * Returns True if the specified record id is selected.
24459      * @param {String} id The id of record to check
24460      * @return {Boolean}
24461      */
24462     isIdSelected : function(id){
24463         return (this.selections.key(id) ? true : false);
24464     },
24465
24466
24467     // private
24468     handleMouseDBClick : function(e, t){
24469         
24470     },
24471     // private
24472     handleMouseDown : function(e, t)
24473     {
24474             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24475         if(this.isLocked() || rowIndex < 0 ){
24476             return;
24477         };
24478         if(e.shiftKey && this.last !== false){
24479             var last = this.last;
24480             this.selectRange(last, rowIndex, e.ctrlKey);
24481             this.last = last; // reset the last
24482             t.focus();
24483     
24484         }else{
24485             var isSelected = this.isSelected(rowIndex);
24486             //Roo.log("select row:" + rowIndex);
24487             if(isSelected){
24488                 this.deselectRow(rowIndex);
24489             } else {
24490                         this.selectRow(rowIndex, true);
24491             }
24492     
24493             /*
24494                 if(e.button !== 0 && isSelected){
24495                 alert('rowIndex 2: ' + rowIndex);
24496                     view.focusRow(rowIndex);
24497                 }else if(e.ctrlKey && isSelected){
24498                     this.deselectRow(rowIndex);
24499                 }else if(!isSelected){
24500                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24501                     view.focusRow(rowIndex);
24502                 }
24503             */
24504         }
24505         this.fireEvent("afterselectionchange", this);
24506     },
24507     // private
24508     handleDragableRowClick :  function(grid, rowIndex, e) 
24509     {
24510         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24511             this.selectRow(rowIndex, false);
24512             grid.view.focusRow(rowIndex);
24513              this.fireEvent("afterselectionchange", this);
24514         }
24515     },
24516     
24517     /**
24518      * Selects multiple rows.
24519      * @param {Array} rows Array of the indexes of the row to select
24520      * @param {Boolean} keepExisting (optional) True to keep existing selections
24521      */
24522     selectRows : function(rows, keepExisting){
24523         if(!keepExisting){
24524             this.clearSelections();
24525         }
24526         for(var i = 0, len = rows.length; i < len; i++){
24527             this.selectRow(rows[i], true);
24528         }
24529     },
24530
24531     /**
24532      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24533      * @param {Number} startRow The index of the first row in the range
24534      * @param {Number} endRow The index of the last row in the range
24535      * @param {Boolean} keepExisting (optional) True to retain existing selections
24536      */
24537     selectRange : function(startRow, endRow, keepExisting){
24538         if(this.locked) {
24539             return;
24540         }
24541         if(!keepExisting){
24542             this.clearSelections();
24543         }
24544         if(startRow <= endRow){
24545             for(var i = startRow; i <= endRow; i++){
24546                 this.selectRow(i, true);
24547             }
24548         }else{
24549             for(var i = startRow; i >= endRow; i--){
24550                 this.selectRow(i, true);
24551             }
24552         }
24553     },
24554
24555     /**
24556      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24557      * @param {Number} startRow The index of the first row in the range
24558      * @param {Number} endRow The index of the last row in the range
24559      */
24560     deselectRange : function(startRow, endRow, preventViewNotify){
24561         if(this.locked) {
24562             return;
24563         }
24564         for(var i = startRow; i <= endRow; i++){
24565             this.deselectRow(i, preventViewNotify);
24566         }
24567     },
24568
24569     /**
24570      * Selects a row.
24571      * @param {Number} row The index of the row to select
24572      * @param {Boolean} keepExisting (optional) True to keep existing selections
24573      */
24574     selectRow : function(index, keepExisting, preventViewNotify)
24575     {
24576             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24577             return;
24578         }
24579         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24580             if(!keepExisting || this.singleSelect){
24581                 this.clearSelections();
24582             }
24583             
24584             var r = this.grid.store.getAt(index);
24585             //console.log('selectRow - record id :' + r.id);
24586             
24587             this.selections.add(r);
24588             this.last = this.lastActive = index;
24589             if(!preventViewNotify){
24590                 var proxy = new Roo.Element(
24591                                 this.grid.getRowDom(index)
24592                 );
24593                 proxy.addClass('bg-info info');
24594             }
24595             this.fireEvent("rowselect", this, index, r);
24596             this.fireEvent("selectionchange", this);
24597         }
24598     },
24599
24600     /**
24601      * Deselects a row.
24602      * @param {Number} row The index of the row to deselect
24603      */
24604     deselectRow : function(index, preventViewNotify)
24605     {
24606         if(this.locked) {
24607             return;
24608         }
24609         if(this.last == index){
24610             this.last = false;
24611         }
24612         if(this.lastActive == index){
24613             this.lastActive = false;
24614         }
24615         
24616         var r = this.grid.store.getAt(index);
24617         if (!r) {
24618             return;
24619         }
24620         
24621         this.selections.remove(r);
24622         //.console.log('deselectRow - record id :' + r.id);
24623         if(!preventViewNotify){
24624         
24625             var proxy = new Roo.Element(
24626                 this.grid.getRowDom(index)
24627             );
24628             proxy.removeClass('bg-info info');
24629         }
24630         this.fireEvent("rowdeselect", this, index);
24631         this.fireEvent("selectionchange", this);
24632     },
24633
24634     // private
24635     restoreLast : function(){
24636         if(this._last){
24637             this.last = this._last;
24638         }
24639     },
24640
24641     // private
24642     acceptsNav : function(row, col, cm){
24643         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24644     },
24645
24646     // private
24647     onEditorKey : function(field, e){
24648         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24649         if(k == e.TAB){
24650             e.stopEvent();
24651             ed.completeEdit();
24652             if(e.shiftKey){
24653                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24654             }else{
24655                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24656             }
24657         }else if(k == e.ENTER && !e.ctrlKey){
24658             e.stopEvent();
24659             ed.completeEdit();
24660             if(e.shiftKey){
24661                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24662             }else{
24663                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24664             }
24665         }else if(k == e.ESC){
24666             ed.cancelEdit();
24667         }
24668         if(newCell){
24669             g.startEditing(newCell[0], newCell[1]);
24670         }
24671     }
24672 });
24673 /*
24674  * Based on:
24675  * Ext JS Library 1.1.1
24676  * Copyright(c) 2006-2007, Ext JS, LLC.
24677  *
24678  * Originally Released Under LGPL - original licence link has changed is not relivant.
24679  *
24680  * Fork - LGPL
24681  * <script type="text/javascript">
24682  */
24683  
24684 /**
24685  * @class Roo.bootstrap.PagingToolbar
24686  * @extends Roo.bootstrap.NavSimplebar
24687  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24688  * @constructor
24689  * Create a new PagingToolbar
24690  * @param {Object} config The config object
24691  * @param {Roo.data.Store} store
24692  */
24693 Roo.bootstrap.PagingToolbar = function(config)
24694 {
24695     // old args format still supported... - xtype is prefered..
24696         // created from xtype...
24697     
24698     this.ds = config.dataSource;
24699     
24700     if (config.store && !this.ds) {
24701         this.store= Roo.factory(config.store, Roo.data);
24702         this.ds = this.store;
24703         this.ds.xmodule = this.xmodule || false;
24704     }
24705     
24706     this.toolbarItems = [];
24707     if (config.items) {
24708         this.toolbarItems = config.items;
24709     }
24710     
24711     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24712     
24713     this.cursor = 0;
24714     
24715     if (this.ds) { 
24716         this.bind(this.ds);
24717     }
24718     
24719     if (Roo.bootstrap.version == 4) {
24720         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24721     } else {
24722         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24723     }
24724     
24725 };
24726
24727 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24728     /**
24729      * @cfg {Roo.data.Store} dataSource
24730      * The underlying data store providing the paged data
24731      */
24732     /**
24733      * @cfg {String/HTMLElement/Element} container
24734      * container The id or element that will contain the toolbar
24735      */
24736     /**
24737      * @cfg {Boolean} displayInfo
24738      * True to display the displayMsg (defaults to false)
24739      */
24740     /**
24741      * @cfg {Number} pageSize
24742      * The number of records to display per page (defaults to 20)
24743      */
24744     pageSize: 20,
24745     /**
24746      * @cfg {String} displayMsg
24747      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24748      */
24749     displayMsg : 'Displaying {0} - {1} of {2}',
24750     /**
24751      * @cfg {String} emptyMsg
24752      * The message to display when no records are found (defaults to "No data to display")
24753      */
24754     emptyMsg : 'No data to display',
24755     /**
24756      * Customizable piece of the default paging text (defaults to "Page")
24757      * @type String
24758      */
24759     beforePageText : "Page",
24760     /**
24761      * Customizable piece of the default paging text (defaults to "of %0")
24762      * @type String
24763      */
24764     afterPageText : "of {0}",
24765     /**
24766      * Customizable piece of the default paging text (defaults to "First Page")
24767      * @type String
24768      */
24769     firstText : "First Page",
24770     /**
24771      * Customizable piece of the default paging text (defaults to "Previous Page")
24772      * @type String
24773      */
24774     prevText : "Previous Page",
24775     /**
24776      * Customizable piece of the default paging text (defaults to "Next Page")
24777      * @type String
24778      */
24779     nextText : "Next Page",
24780     /**
24781      * Customizable piece of the default paging text (defaults to "Last Page")
24782      * @type String
24783      */
24784     lastText : "Last Page",
24785     /**
24786      * Customizable piece of the default paging text (defaults to "Refresh")
24787      * @type String
24788      */
24789     refreshText : "Refresh",
24790
24791     buttons : false,
24792     // private
24793     onRender : function(ct, position) 
24794     {
24795         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24796         this.navgroup.parentId = this.id;
24797         this.navgroup.onRender(this.el, null);
24798         // add the buttons to the navgroup
24799         
24800         if(this.displayInfo){
24801             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24802             this.displayEl = this.el.select('.x-paging-info', true).first();
24803 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24804 //            this.displayEl = navel.el.select('span',true).first();
24805         }
24806         
24807         var _this = this;
24808         
24809         if(this.buttons){
24810             Roo.each(_this.buttons, function(e){ // this might need to use render????
24811                Roo.factory(e).render(_this.el);
24812             });
24813         }
24814             
24815         Roo.each(_this.toolbarItems, function(e) {
24816             _this.navgroup.addItem(e);
24817         });
24818         
24819         
24820         this.first = this.navgroup.addItem({
24821             tooltip: this.firstText,
24822             cls: "prev btn-outline-secondary",
24823             html : ' <i class="fa fa-step-backward"></i>',
24824             disabled: true,
24825             preventDefault: true,
24826             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24827         });
24828         
24829         this.prev =  this.navgroup.addItem({
24830             tooltip: this.prevText,
24831             cls: "prev btn-outline-secondary",
24832             html : ' <i class="fa fa-backward"></i>',
24833             disabled: true,
24834             preventDefault: true,
24835             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24836         });
24837     //this.addSeparator();
24838         
24839         
24840         var field = this.navgroup.addItem( {
24841             tagtype : 'span',
24842             cls : 'x-paging-position  btn-outline-secondary',
24843              disabled: true,
24844             html : this.beforePageText  +
24845                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24846                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24847          } ); //?? escaped?
24848         
24849         this.field = field.el.select('input', true).first();
24850         this.field.on("keydown", this.onPagingKeydown, this);
24851         this.field.on("focus", function(){this.dom.select();});
24852     
24853     
24854         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24855         //this.field.setHeight(18);
24856         //this.addSeparator();
24857         this.next = this.navgroup.addItem({
24858             tooltip: this.nextText,
24859             cls: "next btn-outline-secondary",
24860             html : ' <i class="fa fa-forward"></i>',
24861             disabled: true,
24862             preventDefault: true,
24863             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24864         });
24865         this.last = this.navgroup.addItem({
24866             tooltip: this.lastText,
24867             html : ' <i class="fa fa-step-forward"></i>',
24868             cls: "next btn-outline-secondary",
24869             disabled: true,
24870             preventDefault: true,
24871             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24872         });
24873     //this.addSeparator();
24874         this.loading = this.navgroup.addItem({
24875             tooltip: this.refreshText,
24876             cls: "btn-outline-secondary",
24877             html : ' <i class="fa fa-refresh"></i>',
24878             preventDefault: true,
24879             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24880         });
24881         
24882     },
24883
24884     // private
24885     updateInfo : function(){
24886         if(this.displayEl){
24887             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24888             var msg = count == 0 ?
24889                 this.emptyMsg :
24890                 String.format(
24891                     this.displayMsg,
24892                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24893                 );
24894             this.displayEl.update(msg);
24895         }
24896     },
24897
24898     // private
24899     onLoad : function(ds, r, o)
24900     {
24901         this.cursor = o.params.start ? o.params.start : 0;
24902         
24903         var d = this.getPageData(),
24904             ap = d.activePage,
24905             ps = d.pages;
24906         
24907         
24908         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24909         this.field.dom.value = ap;
24910         this.first.setDisabled(ap == 1);
24911         this.prev.setDisabled(ap == 1);
24912         this.next.setDisabled(ap == ps);
24913         this.last.setDisabled(ap == ps);
24914         this.loading.enable();
24915         this.updateInfo();
24916     },
24917
24918     // private
24919     getPageData : function(){
24920         var total = this.ds.getTotalCount();
24921         return {
24922             total : total,
24923             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24924             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24925         };
24926     },
24927
24928     // private
24929     onLoadError : function(){
24930         this.loading.enable();
24931     },
24932
24933     // private
24934     onPagingKeydown : function(e){
24935         var k = e.getKey();
24936         var d = this.getPageData();
24937         if(k == e.RETURN){
24938             var v = this.field.dom.value, pageNum;
24939             if(!v || isNaN(pageNum = parseInt(v, 10))){
24940                 this.field.dom.value = d.activePage;
24941                 return;
24942             }
24943             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24944             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24945             e.stopEvent();
24946         }
24947         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))
24948         {
24949           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24950           this.field.dom.value = pageNum;
24951           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24952           e.stopEvent();
24953         }
24954         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24955         {
24956           var v = this.field.dom.value, pageNum; 
24957           var increment = (e.shiftKey) ? 10 : 1;
24958           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24959                 increment *= -1;
24960           }
24961           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24962             this.field.dom.value = d.activePage;
24963             return;
24964           }
24965           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24966           {
24967             this.field.dom.value = parseInt(v, 10) + increment;
24968             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24969             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24970           }
24971           e.stopEvent();
24972         }
24973     },
24974
24975     // private
24976     beforeLoad : function(){
24977         if(this.loading){
24978             this.loading.disable();
24979         }
24980     },
24981
24982     // private
24983     onClick : function(which){
24984         
24985         var ds = this.ds;
24986         if (!ds) {
24987             return;
24988         }
24989         
24990         switch(which){
24991             case "first":
24992                 ds.load({params:{start: 0, limit: this.pageSize}});
24993             break;
24994             case "prev":
24995                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24996             break;
24997             case "next":
24998                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24999             break;
25000             case "last":
25001                 var total = ds.getTotalCount();
25002                 var extra = total % this.pageSize;
25003                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25004                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25005             break;
25006             case "refresh":
25007                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25008             break;
25009         }
25010     },
25011
25012     /**
25013      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25014      * @param {Roo.data.Store} store The data store to unbind
25015      */
25016     unbind : function(ds){
25017         ds.un("beforeload", this.beforeLoad, this);
25018         ds.un("load", this.onLoad, this);
25019         ds.un("loadexception", this.onLoadError, this);
25020         ds.un("remove", this.updateInfo, this);
25021         ds.un("add", this.updateInfo, this);
25022         this.ds = undefined;
25023     },
25024
25025     /**
25026      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25027      * @param {Roo.data.Store} store The data store to bind
25028      */
25029     bind : function(ds){
25030         ds.on("beforeload", this.beforeLoad, this);
25031         ds.on("load", this.onLoad, this);
25032         ds.on("loadexception", this.onLoadError, this);
25033         ds.on("remove", this.updateInfo, this);
25034         ds.on("add", this.updateInfo, this);
25035         this.ds = ds;
25036     }
25037 });/*
25038  * - LGPL
25039  *
25040  * element
25041  * 
25042  */
25043
25044 /**
25045  * @class Roo.bootstrap.MessageBar
25046  * @extends Roo.bootstrap.Component
25047  * Bootstrap MessageBar class
25048  * @cfg {String} html contents of the MessageBar
25049  * @cfg {String} weight (info | success | warning | danger) default info
25050  * @cfg {String} beforeClass insert the bar before the given class
25051  * @cfg {Boolean} closable (true | false) default false
25052  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25053  * 
25054  * @constructor
25055  * Create a new Element
25056  * @param {Object} config The config object
25057  */
25058
25059 Roo.bootstrap.MessageBar = function(config){
25060     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25061 };
25062
25063 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25064     
25065     html: '',
25066     weight: 'info',
25067     closable: false,
25068     fixed: false,
25069     beforeClass: 'bootstrap-sticky-wrap',
25070     
25071     getAutoCreate : function(){
25072         
25073         var cfg = {
25074             tag: 'div',
25075             cls: 'alert alert-dismissable alert-' + this.weight,
25076             cn: [
25077                 {
25078                     tag: 'span',
25079                     cls: 'message',
25080                     html: this.html || ''
25081                 }
25082             ]
25083         };
25084         
25085         if(this.fixed){
25086             cfg.cls += ' alert-messages-fixed';
25087         }
25088         
25089         if(this.closable){
25090             cfg.cn.push({
25091                 tag: 'button',
25092                 cls: 'close',
25093                 html: 'x'
25094             });
25095         }
25096         
25097         return cfg;
25098     },
25099     
25100     onRender : function(ct, position)
25101     {
25102         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25103         
25104         if(!this.el){
25105             var cfg = Roo.apply({},  this.getAutoCreate());
25106             cfg.id = Roo.id();
25107             
25108             if (this.cls) {
25109                 cfg.cls += ' ' + this.cls;
25110             }
25111             if (this.style) {
25112                 cfg.style = this.style;
25113             }
25114             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25115             
25116             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25117         }
25118         
25119         this.el.select('>button.close').on('click', this.hide, this);
25120         
25121     },
25122     
25123     show : function()
25124     {
25125         if (!this.rendered) {
25126             this.render();
25127         }
25128         
25129         this.el.show();
25130         
25131         this.fireEvent('show', this);
25132         
25133     },
25134     
25135     hide : function()
25136     {
25137         if (!this.rendered) {
25138             this.render();
25139         }
25140         
25141         this.el.hide();
25142         
25143         this.fireEvent('hide', this);
25144     },
25145     
25146     update : function()
25147     {
25148 //        var e = this.el.dom.firstChild;
25149 //        
25150 //        if(this.closable){
25151 //            e = e.nextSibling;
25152 //        }
25153 //        
25154 //        e.data = this.html || '';
25155
25156         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25157     }
25158    
25159 });
25160
25161  
25162
25163      /*
25164  * - LGPL
25165  *
25166  * Graph
25167  * 
25168  */
25169
25170
25171 /**
25172  * @class Roo.bootstrap.Graph
25173  * @extends Roo.bootstrap.Component
25174  * Bootstrap Graph class
25175 > Prameters
25176  -sm {number} sm 4
25177  -md {number} md 5
25178  @cfg {String} graphtype  bar | vbar | pie
25179  @cfg {number} g_x coodinator | centre x (pie)
25180  @cfg {number} g_y coodinator | centre y (pie)
25181  @cfg {number} g_r radius (pie)
25182  @cfg {number} g_height height of the chart (respected by all elements in the set)
25183  @cfg {number} g_width width of the chart (respected by all elements in the set)
25184  @cfg {Object} title The title of the chart
25185     
25186  -{Array}  values
25187  -opts (object) options for the chart 
25188      o {
25189      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25190      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25191      o vgutter (number)
25192      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.
25193      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25194      o to
25195      o stretch (boolean)
25196      o }
25197  -opts (object) options for the pie
25198      o{
25199      o cut
25200      o startAngle (number)
25201      o endAngle (number)
25202      } 
25203  *
25204  * @constructor
25205  * Create a new Input
25206  * @param {Object} config The config object
25207  */
25208
25209 Roo.bootstrap.Graph = function(config){
25210     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25211     
25212     this.addEvents({
25213         // img events
25214         /**
25215          * @event click
25216          * The img click event for the img.
25217          * @param {Roo.EventObject} e
25218          */
25219         "click" : true
25220     });
25221 };
25222
25223 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25224     
25225     sm: 4,
25226     md: 5,
25227     graphtype: 'bar',
25228     g_height: 250,
25229     g_width: 400,
25230     g_x: 50,
25231     g_y: 50,
25232     g_r: 30,
25233     opts:{
25234         //g_colors: this.colors,
25235         g_type: 'soft',
25236         g_gutter: '20%'
25237
25238     },
25239     title : false,
25240
25241     getAutoCreate : function(){
25242         
25243         var cfg = {
25244             tag: 'div',
25245             html : null
25246         };
25247         
25248         
25249         return  cfg;
25250     },
25251
25252     onRender : function(ct,position){
25253         
25254         
25255         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25256         
25257         if (typeof(Raphael) == 'undefined') {
25258             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25259             return;
25260         }
25261         
25262         this.raphael = Raphael(this.el.dom);
25263         
25264                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25265                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25266                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25267                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25268                 /*
25269                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25270                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25271                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25272                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25273                 
25274                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25275                 r.barchart(330, 10, 300, 220, data1);
25276                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25277                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25278                 */
25279                 
25280                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25281                 // r.barchart(30, 30, 560, 250,  xdata, {
25282                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25283                 //     axis : "0 0 1 1",
25284                 //     axisxlabels :  xdata
25285                 //     //yvalues : cols,
25286                    
25287                 // });
25288 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25289 //        
25290 //        this.load(null,xdata,{
25291 //                axis : "0 0 1 1",
25292 //                axisxlabels :  xdata
25293 //                });
25294
25295     },
25296
25297     load : function(graphtype,xdata,opts)
25298     {
25299         this.raphael.clear();
25300         if(!graphtype) {
25301             graphtype = this.graphtype;
25302         }
25303         if(!opts){
25304             opts = this.opts;
25305         }
25306         var r = this.raphael,
25307             fin = function () {
25308                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25309             },
25310             fout = function () {
25311                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25312             },
25313             pfin = function() {
25314                 this.sector.stop();
25315                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25316
25317                 if (this.label) {
25318                     this.label[0].stop();
25319                     this.label[0].attr({ r: 7.5 });
25320                     this.label[1].attr({ "font-weight": 800 });
25321                 }
25322             },
25323             pfout = function() {
25324                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25325
25326                 if (this.label) {
25327                     this.label[0].animate({ r: 5 }, 500, "bounce");
25328                     this.label[1].attr({ "font-weight": 400 });
25329                 }
25330             };
25331
25332         switch(graphtype){
25333             case 'bar':
25334                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25335                 break;
25336             case 'hbar':
25337                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25338                 break;
25339             case 'pie':
25340 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25341 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25342 //            
25343                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25344                 
25345                 break;
25346
25347         }
25348         
25349         if(this.title){
25350             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25351         }
25352         
25353     },
25354     
25355     setTitle: function(o)
25356     {
25357         this.title = o;
25358     },
25359     
25360     initEvents: function() {
25361         
25362         if(!this.href){
25363             this.el.on('click', this.onClick, this);
25364         }
25365     },
25366     
25367     onClick : function(e)
25368     {
25369         Roo.log('img onclick');
25370         this.fireEvent('click', this, e);
25371     }
25372    
25373 });
25374
25375  
25376 /*
25377  * - LGPL
25378  *
25379  * numberBox
25380  * 
25381  */
25382 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25383
25384 /**
25385  * @class Roo.bootstrap.dash.NumberBox
25386  * @extends Roo.bootstrap.Component
25387  * Bootstrap NumberBox class
25388  * @cfg {String} headline Box headline
25389  * @cfg {String} content Box content
25390  * @cfg {String} icon Box icon
25391  * @cfg {String} footer Footer text
25392  * @cfg {String} fhref Footer href
25393  * 
25394  * @constructor
25395  * Create a new NumberBox
25396  * @param {Object} config The config object
25397  */
25398
25399
25400 Roo.bootstrap.dash.NumberBox = function(config){
25401     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25402     
25403 };
25404
25405 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25406     
25407     headline : '',
25408     content : '',
25409     icon : '',
25410     footer : '',
25411     fhref : '',
25412     ficon : '',
25413     
25414     getAutoCreate : function(){
25415         
25416         var cfg = {
25417             tag : 'div',
25418             cls : 'small-box ',
25419             cn : [
25420                 {
25421                     tag : 'div',
25422                     cls : 'inner',
25423                     cn :[
25424                         {
25425                             tag : 'h3',
25426                             cls : 'roo-headline',
25427                             html : this.headline
25428                         },
25429                         {
25430                             tag : 'p',
25431                             cls : 'roo-content',
25432                             html : this.content
25433                         }
25434                     ]
25435                 }
25436             ]
25437         };
25438         
25439         if(this.icon){
25440             cfg.cn.push({
25441                 tag : 'div',
25442                 cls : 'icon',
25443                 cn :[
25444                     {
25445                         tag : 'i',
25446                         cls : 'ion ' + this.icon
25447                     }
25448                 ]
25449             });
25450         }
25451         
25452         if(this.footer){
25453             var footer = {
25454                 tag : 'a',
25455                 cls : 'small-box-footer',
25456                 href : this.fhref || '#',
25457                 html : this.footer
25458             };
25459             
25460             cfg.cn.push(footer);
25461             
25462         }
25463         
25464         return  cfg;
25465     },
25466
25467     onRender : function(ct,position){
25468         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25469
25470
25471        
25472                 
25473     },
25474
25475     setHeadline: function (value)
25476     {
25477         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25478     },
25479     
25480     setFooter: function (value, href)
25481     {
25482         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25483         
25484         if(href){
25485             this.el.select('a.small-box-footer',true).first().attr('href', href);
25486         }
25487         
25488     },
25489
25490     setContent: function (value)
25491     {
25492         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25493     },
25494
25495     initEvents: function() 
25496     {   
25497         
25498     }
25499     
25500 });
25501
25502  
25503 /*
25504  * - LGPL
25505  *
25506  * TabBox
25507  * 
25508  */
25509 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25510
25511 /**
25512  * @class Roo.bootstrap.dash.TabBox
25513  * @extends Roo.bootstrap.Component
25514  * Bootstrap TabBox class
25515  * @cfg {String} title Title of the TabBox
25516  * @cfg {String} icon Icon of the TabBox
25517  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25518  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25519  * 
25520  * @constructor
25521  * Create a new TabBox
25522  * @param {Object} config The config object
25523  */
25524
25525
25526 Roo.bootstrap.dash.TabBox = function(config){
25527     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25528     this.addEvents({
25529         // raw events
25530         /**
25531          * @event addpane
25532          * When a pane is added
25533          * @param {Roo.bootstrap.dash.TabPane} pane
25534          */
25535         "addpane" : true,
25536         /**
25537          * @event activatepane
25538          * When a pane is activated
25539          * @param {Roo.bootstrap.dash.TabPane} pane
25540          */
25541         "activatepane" : true
25542         
25543          
25544     });
25545     
25546     this.panes = [];
25547 };
25548
25549 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25550
25551     title : '',
25552     icon : false,
25553     showtabs : true,
25554     tabScrollable : false,
25555     
25556     getChildContainer : function()
25557     {
25558         return this.el.select('.tab-content', true).first();
25559     },
25560     
25561     getAutoCreate : function(){
25562         
25563         var header = {
25564             tag: 'li',
25565             cls: 'pull-left header',
25566             html: this.title,
25567             cn : []
25568         };
25569         
25570         if(this.icon){
25571             header.cn.push({
25572                 tag: 'i',
25573                 cls: 'fa ' + this.icon
25574             });
25575         }
25576         
25577         var h = {
25578             tag: 'ul',
25579             cls: 'nav nav-tabs pull-right',
25580             cn: [
25581                 header
25582             ]
25583         };
25584         
25585         if(this.tabScrollable){
25586             h = {
25587                 tag: 'div',
25588                 cls: 'tab-header',
25589                 cn: [
25590                     {
25591                         tag: 'ul',
25592                         cls: 'nav nav-tabs pull-right',
25593                         cn: [
25594                             header
25595                         ]
25596                     }
25597                 ]
25598             };
25599         }
25600         
25601         var cfg = {
25602             tag: 'div',
25603             cls: 'nav-tabs-custom',
25604             cn: [
25605                 h,
25606                 {
25607                     tag: 'div',
25608                     cls: 'tab-content no-padding',
25609                     cn: []
25610                 }
25611             ]
25612         };
25613
25614         return  cfg;
25615     },
25616     initEvents : function()
25617     {
25618         //Roo.log('add add pane handler');
25619         this.on('addpane', this.onAddPane, this);
25620     },
25621      /**
25622      * Updates the box title
25623      * @param {String} html to set the title to.
25624      */
25625     setTitle : function(value)
25626     {
25627         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25628     },
25629     onAddPane : function(pane)
25630     {
25631         this.panes.push(pane);
25632         //Roo.log('addpane');
25633         //Roo.log(pane);
25634         // tabs are rendere left to right..
25635         if(!this.showtabs){
25636             return;
25637         }
25638         
25639         var ctr = this.el.select('.nav-tabs', true).first();
25640          
25641          
25642         var existing = ctr.select('.nav-tab',true);
25643         var qty = existing.getCount();;
25644         
25645         
25646         var tab = ctr.createChild({
25647             tag : 'li',
25648             cls : 'nav-tab' + (qty ? '' : ' active'),
25649             cn : [
25650                 {
25651                     tag : 'a',
25652                     href:'#',
25653                     html : pane.title
25654                 }
25655             ]
25656         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25657         pane.tab = tab;
25658         
25659         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25660         if (!qty) {
25661             pane.el.addClass('active');
25662         }
25663         
25664                 
25665     },
25666     onTabClick : function(ev,un,ob,pane)
25667     {
25668         //Roo.log('tab - prev default');
25669         ev.preventDefault();
25670         
25671         
25672         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25673         pane.tab.addClass('active');
25674         //Roo.log(pane.title);
25675         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25676         // technically we should have a deactivate event.. but maybe add later.
25677         // and it should not de-activate the selected tab...
25678         this.fireEvent('activatepane', pane);
25679         pane.el.addClass('active');
25680         pane.fireEvent('activate');
25681         
25682         
25683     },
25684     
25685     getActivePane : function()
25686     {
25687         var r = false;
25688         Roo.each(this.panes, function(p) {
25689             if(p.el.hasClass('active')){
25690                 r = p;
25691                 return false;
25692             }
25693             
25694             return;
25695         });
25696         
25697         return r;
25698     }
25699     
25700     
25701 });
25702
25703  
25704 /*
25705  * - LGPL
25706  *
25707  * Tab pane
25708  * 
25709  */
25710 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25711 /**
25712  * @class Roo.bootstrap.TabPane
25713  * @extends Roo.bootstrap.Component
25714  * Bootstrap TabPane class
25715  * @cfg {Boolean} active (false | true) Default false
25716  * @cfg {String} title title of panel
25717
25718  * 
25719  * @constructor
25720  * Create a new TabPane
25721  * @param {Object} config The config object
25722  */
25723
25724 Roo.bootstrap.dash.TabPane = function(config){
25725     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25726     
25727     this.addEvents({
25728         // raw events
25729         /**
25730          * @event activate
25731          * When a pane is activated
25732          * @param {Roo.bootstrap.dash.TabPane} pane
25733          */
25734         "activate" : true
25735          
25736     });
25737 };
25738
25739 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25740     
25741     active : false,
25742     title : '',
25743     
25744     // the tabBox that this is attached to.
25745     tab : false,
25746      
25747     getAutoCreate : function() 
25748     {
25749         var cfg = {
25750             tag: 'div',
25751             cls: 'tab-pane'
25752         };
25753         
25754         if(this.active){
25755             cfg.cls += ' active';
25756         }
25757         
25758         return cfg;
25759     },
25760     initEvents  : function()
25761     {
25762         //Roo.log('trigger add pane handler');
25763         this.parent().fireEvent('addpane', this)
25764     },
25765     
25766      /**
25767      * Updates the tab title 
25768      * @param {String} html to set the title to.
25769      */
25770     setTitle: function(str)
25771     {
25772         if (!this.tab) {
25773             return;
25774         }
25775         this.title = str;
25776         this.tab.select('a', true).first().dom.innerHTML = str;
25777         
25778     }
25779     
25780     
25781     
25782 });
25783
25784  
25785
25786
25787  /*
25788  * - LGPL
25789  *
25790  * menu
25791  * 
25792  */
25793 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25794
25795 /**
25796  * @class Roo.bootstrap.menu.Menu
25797  * @extends Roo.bootstrap.Component
25798  * Bootstrap Menu class - container for Menu
25799  * @cfg {String} html Text of the menu
25800  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25801  * @cfg {String} icon Font awesome icon
25802  * @cfg {String} pos Menu align to (top | bottom) default bottom
25803  * 
25804  * 
25805  * @constructor
25806  * Create a new Menu
25807  * @param {Object} config The config object
25808  */
25809
25810
25811 Roo.bootstrap.menu.Menu = function(config){
25812     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25813     
25814     this.addEvents({
25815         /**
25816          * @event beforeshow
25817          * Fires before this menu is displayed
25818          * @param {Roo.bootstrap.menu.Menu} this
25819          */
25820         beforeshow : true,
25821         /**
25822          * @event beforehide
25823          * Fires before this menu is hidden
25824          * @param {Roo.bootstrap.menu.Menu} this
25825          */
25826         beforehide : true,
25827         /**
25828          * @event show
25829          * Fires after this menu is displayed
25830          * @param {Roo.bootstrap.menu.Menu} this
25831          */
25832         show : true,
25833         /**
25834          * @event hide
25835          * Fires after this menu is hidden
25836          * @param {Roo.bootstrap.menu.Menu} this
25837          */
25838         hide : true,
25839         /**
25840          * @event click
25841          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25842          * @param {Roo.bootstrap.menu.Menu} this
25843          * @param {Roo.EventObject} e
25844          */
25845         click : true
25846     });
25847     
25848 };
25849
25850 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25851     
25852     submenu : false,
25853     html : '',
25854     weight : 'default',
25855     icon : false,
25856     pos : 'bottom',
25857     
25858     
25859     getChildContainer : function() {
25860         if(this.isSubMenu){
25861             return this.el;
25862         }
25863         
25864         return this.el.select('ul.dropdown-menu', true).first();  
25865     },
25866     
25867     getAutoCreate : function()
25868     {
25869         var text = [
25870             {
25871                 tag : 'span',
25872                 cls : 'roo-menu-text',
25873                 html : this.html
25874             }
25875         ];
25876         
25877         if(this.icon){
25878             text.unshift({
25879                 tag : 'i',
25880                 cls : 'fa ' + this.icon
25881             })
25882         }
25883         
25884         
25885         var cfg = {
25886             tag : 'div',
25887             cls : 'btn-group',
25888             cn : [
25889                 {
25890                     tag : 'button',
25891                     cls : 'dropdown-button btn btn-' + this.weight,
25892                     cn : text
25893                 },
25894                 {
25895                     tag : 'button',
25896                     cls : 'dropdown-toggle btn btn-' + this.weight,
25897                     cn : [
25898                         {
25899                             tag : 'span',
25900                             cls : 'caret'
25901                         }
25902                     ]
25903                 },
25904                 {
25905                     tag : 'ul',
25906                     cls : 'dropdown-menu'
25907                 }
25908             ]
25909             
25910         };
25911         
25912         if(this.pos == 'top'){
25913             cfg.cls += ' dropup';
25914         }
25915         
25916         if(this.isSubMenu){
25917             cfg = {
25918                 tag : 'ul',
25919                 cls : 'dropdown-menu'
25920             }
25921         }
25922         
25923         return cfg;
25924     },
25925     
25926     onRender : function(ct, position)
25927     {
25928         this.isSubMenu = ct.hasClass('dropdown-submenu');
25929         
25930         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25931     },
25932     
25933     initEvents : function() 
25934     {
25935         if(this.isSubMenu){
25936             return;
25937         }
25938         
25939         this.hidden = true;
25940         
25941         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25942         this.triggerEl.on('click', this.onTriggerPress, this);
25943         
25944         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25945         this.buttonEl.on('click', this.onClick, this);
25946         
25947     },
25948     
25949     list : function()
25950     {
25951         if(this.isSubMenu){
25952             return this.el;
25953         }
25954         
25955         return this.el.select('ul.dropdown-menu', true).first();
25956     },
25957     
25958     onClick : function(e)
25959     {
25960         this.fireEvent("click", this, e);
25961     },
25962     
25963     onTriggerPress  : function(e)
25964     {   
25965         if (this.isVisible()) {
25966             this.hide();
25967         } else {
25968             this.show();
25969         }
25970     },
25971     
25972     isVisible : function(){
25973         return !this.hidden;
25974     },
25975     
25976     show : function()
25977     {
25978         this.fireEvent("beforeshow", this);
25979         
25980         this.hidden = false;
25981         this.el.addClass('open');
25982         
25983         Roo.get(document).on("mouseup", this.onMouseUp, this);
25984         
25985         this.fireEvent("show", this);
25986         
25987         
25988     },
25989     
25990     hide : function()
25991     {
25992         this.fireEvent("beforehide", this);
25993         
25994         this.hidden = true;
25995         this.el.removeClass('open');
25996         
25997         Roo.get(document).un("mouseup", this.onMouseUp);
25998         
25999         this.fireEvent("hide", this);
26000     },
26001     
26002     onMouseUp : function()
26003     {
26004         this.hide();
26005     }
26006     
26007 });
26008
26009  
26010  /*
26011  * - LGPL
26012  *
26013  * menu item
26014  * 
26015  */
26016 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26017
26018 /**
26019  * @class Roo.bootstrap.menu.Item
26020  * @extends Roo.bootstrap.Component
26021  * Bootstrap MenuItem class
26022  * @cfg {Boolean} submenu (true | false) default false
26023  * @cfg {String} html text of the item
26024  * @cfg {String} href the link
26025  * @cfg {Boolean} disable (true | false) default false
26026  * @cfg {Boolean} preventDefault (true | false) default true
26027  * @cfg {String} icon Font awesome icon
26028  * @cfg {String} pos Submenu align to (left | right) default right 
26029  * 
26030  * 
26031  * @constructor
26032  * Create a new Item
26033  * @param {Object} config The config object
26034  */
26035
26036
26037 Roo.bootstrap.menu.Item = function(config){
26038     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26039     this.addEvents({
26040         /**
26041          * @event mouseover
26042          * Fires when the mouse is hovering over this menu
26043          * @param {Roo.bootstrap.menu.Item} this
26044          * @param {Roo.EventObject} e
26045          */
26046         mouseover : true,
26047         /**
26048          * @event mouseout
26049          * Fires when the mouse exits this menu
26050          * @param {Roo.bootstrap.menu.Item} this
26051          * @param {Roo.EventObject} e
26052          */
26053         mouseout : true,
26054         // raw events
26055         /**
26056          * @event click
26057          * The raw click event for the entire grid.
26058          * @param {Roo.EventObject} e
26059          */
26060         click : true
26061     });
26062 };
26063
26064 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26065     
26066     submenu : false,
26067     href : '',
26068     html : '',
26069     preventDefault: true,
26070     disable : false,
26071     icon : false,
26072     pos : 'right',
26073     
26074     getAutoCreate : function()
26075     {
26076         var text = [
26077             {
26078                 tag : 'span',
26079                 cls : 'roo-menu-item-text',
26080                 html : this.html
26081             }
26082         ];
26083         
26084         if(this.icon){
26085             text.unshift({
26086                 tag : 'i',
26087                 cls : 'fa ' + this.icon
26088             })
26089         }
26090         
26091         var cfg = {
26092             tag : 'li',
26093             cn : [
26094                 {
26095                     tag : 'a',
26096                     href : this.href || '#',
26097                     cn : text
26098                 }
26099             ]
26100         };
26101         
26102         if(this.disable){
26103             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26104         }
26105         
26106         if(this.submenu){
26107             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26108             
26109             if(this.pos == 'left'){
26110                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26111             }
26112         }
26113         
26114         return cfg;
26115     },
26116     
26117     initEvents : function() 
26118     {
26119         this.el.on('mouseover', this.onMouseOver, this);
26120         this.el.on('mouseout', this.onMouseOut, this);
26121         
26122         this.el.select('a', true).first().on('click', this.onClick, this);
26123         
26124     },
26125     
26126     onClick : function(e)
26127     {
26128         if(this.preventDefault){
26129             e.preventDefault();
26130         }
26131         
26132         this.fireEvent("click", this, e);
26133     },
26134     
26135     onMouseOver : function(e)
26136     {
26137         if(this.submenu && this.pos == 'left'){
26138             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26139         }
26140         
26141         this.fireEvent("mouseover", this, e);
26142     },
26143     
26144     onMouseOut : function(e)
26145     {
26146         this.fireEvent("mouseout", this, e);
26147     }
26148 });
26149
26150  
26151
26152  /*
26153  * - LGPL
26154  *
26155  * menu separator
26156  * 
26157  */
26158 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26159
26160 /**
26161  * @class Roo.bootstrap.menu.Separator
26162  * @extends Roo.bootstrap.Component
26163  * Bootstrap Separator class
26164  * 
26165  * @constructor
26166  * Create a new Separator
26167  * @param {Object} config The config object
26168  */
26169
26170
26171 Roo.bootstrap.menu.Separator = function(config){
26172     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26173 };
26174
26175 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26176     
26177     getAutoCreate : function(){
26178         var cfg = {
26179             tag : 'li',
26180             cls: 'divider'
26181         };
26182         
26183         return cfg;
26184     }
26185    
26186 });
26187
26188  
26189
26190  /*
26191  * - LGPL
26192  *
26193  * Tooltip
26194  * 
26195  */
26196
26197 /**
26198  * @class Roo.bootstrap.Tooltip
26199  * Bootstrap Tooltip class
26200  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26201  * to determine which dom element triggers the tooltip.
26202  * 
26203  * It needs to add support for additional attributes like tooltip-position
26204  * 
26205  * @constructor
26206  * Create a new Toolti
26207  * @param {Object} config The config object
26208  */
26209
26210 Roo.bootstrap.Tooltip = function(config){
26211     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26212     
26213     this.alignment = Roo.bootstrap.Tooltip.alignment;
26214     
26215     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26216         this.alignment = config.alignment;
26217     }
26218     
26219 };
26220
26221 Roo.apply(Roo.bootstrap.Tooltip, {
26222     /**
26223      * @function init initialize tooltip monitoring.
26224      * @static
26225      */
26226     currentEl : false,
26227     currentTip : false,
26228     currentRegion : false,
26229     
26230     //  init : delay?
26231     
26232     init : function()
26233     {
26234         Roo.get(document).on('mouseover', this.enter ,this);
26235         Roo.get(document).on('mouseout', this.leave, this);
26236          
26237         
26238         this.currentTip = new Roo.bootstrap.Tooltip();
26239     },
26240     
26241     enter : function(ev)
26242     {
26243         var dom = ev.getTarget();
26244         
26245         //Roo.log(['enter',dom]);
26246         var el = Roo.fly(dom);
26247         if (this.currentEl) {
26248             //Roo.log(dom);
26249             //Roo.log(this.currentEl);
26250             //Roo.log(this.currentEl.contains(dom));
26251             if (this.currentEl == el) {
26252                 return;
26253             }
26254             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26255                 return;
26256             }
26257
26258         }
26259         
26260         if (this.currentTip.el) {
26261             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26262         }    
26263         //Roo.log(ev);
26264         
26265         if(!el || el.dom == document){
26266             return;
26267         }
26268         
26269         var bindEl = el;
26270         
26271         // you can not look for children, as if el is the body.. then everythign is the child..
26272         if (!el.attr('tooltip')) { //
26273             if (!el.select("[tooltip]").elements.length) {
26274                 return;
26275             }
26276             // is the mouse over this child...?
26277             bindEl = el.select("[tooltip]").first();
26278             var xy = ev.getXY();
26279             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26280                 //Roo.log("not in region.");
26281                 return;
26282             }
26283             //Roo.log("child element over..");
26284             
26285         }
26286         this.currentEl = bindEl;
26287         this.currentTip.bind(bindEl);
26288         this.currentRegion = Roo.lib.Region.getRegion(dom);
26289         this.currentTip.enter();
26290         
26291     },
26292     leave : function(ev)
26293     {
26294         var dom = ev.getTarget();
26295         //Roo.log(['leave',dom]);
26296         if (!this.currentEl) {
26297             return;
26298         }
26299         
26300         
26301         if (dom != this.currentEl.dom) {
26302             return;
26303         }
26304         var xy = ev.getXY();
26305         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26306             return;
26307         }
26308         // only activate leave if mouse cursor is outside... bounding box..
26309         
26310         
26311         
26312         
26313         if (this.currentTip) {
26314             this.currentTip.leave();
26315         }
26316         //Roo.log('clear currentEl');
26317         this.currentEl = false;
26318         
26319         
26320     },
26321     alignment : {
26322         'left' : ['r-l', [-2,0], 'right'],
26323         'right' : ['l-r', [2,0], 'left'],
26324         'bottom' : ['t-b', [0,2], 'top'],
26325         'top' : [ 'b-t', [0,-2], 'bottom']
26326     }
26327     
26328 });
26329
26330
26331 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26332     
26333     
26334     bindEl : false,
26335     
26336     delay : null, // can be { show : 300 , hide: 500}
26337     
26338     timeout : null,
26339     
26340     hoverState : null, //???
26341     
26342     placement : 'bottom', 
26343     
26344     alignment : false,
26345     
26346     getAutoCreate : function(){
26347     
26348         var cfg = {
26349            cls : 'tooltip',
26350            role : 'tooltip',
26351            cn : [
26352                 {
26353                     cls : 'tooltip-arrow'
26354                 },
26355                 {
26356                     cls : 'tooltip-inner'
26357                 }
26358            ]
26359         };
26360         
26361         return cfg;
26362     },
26363     bind : function(el)
26364     {
26365         this.bindEl = el;
26366     },
26367       
26368     
26369     enter : function () {
26370        
26371         if (this.timeout != null) {
26372             clearTimeout(this.timeout);
26373         }
26374         
26375         this.hoverState = 'in';
26376          //Roo.log("enter - show");
26377         if (!this.delay || !this.delay.show) {
26378             this.show();
26379             return;
26380         }
26381         var _t = this;
26382         this.timeout = setTimeout(function () {
26383             if (_t.hoverState == 'in') {
26384                 _t.show();
26385             }
26386         }, this.delay.show);
26387     },
26388     leave : function()
26389     {
26390         clearTimeout(this.timeout);
26391     
26392         this.hoverState = 'out';
26393          if (!this.delay || !this.delay.hide) {
26394             this.hide();
26395             return;
26396         }
26397        
26398         var _t = this;
26399         this.timeout = setTimeout(function () {
26400             //Roo.log("leave - timeout");
26401             
26402             if (_t.hoverState == 'out') {
26403                 _t.hide();
26404                 Roo.bootstrap.Tooltip.currentEl = false;
26405             }
26406         }, delay);
26407     },
26408     
26409     show : function (msg)
26410     {
26411         if (!this.el) {
26412             this.render(document.body);
26413         }
26414         // set content.
26415         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26416         
26417         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26418         
26419         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26420         
26421         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26422         
26423         var placement = typeof this.placement == 'function' ?
26424             this.placement.call(this, this.el, on_el) :
26425             this.placement;
26426             
26427         var autoToken = /\s?auto?\s?/i;
26428         var autoPlace = autoToken.test(placement);
26429         if (autoPlace) {
26430             placement = placement.replace(autoToken, '') || 'top';
26431         }
26432         
26433         //this.el.detach()
26434         //this.el.setXY([0,0]);
26435         this.el.show();
26436         //this.el.dom.style.display='block';
26437         
26438         //this.el.appendTo(on_el);
26439         
26440         var p = this.getPosition();
26441         var box = this.el.getBox();
26442         
26443         if (autoPlace) {
26444             // fixme..
26445         }
26446         
26447         var align = this.alignment[placement];
26448         
26449         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26450         
26451         if(placement == 'top' || placement == 'bottom'){
26452             if(xy[0] < 0){
26453                 placement = 'right';
26454             }
26455             
26456             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26457                 placement = 'left';
26458             }
26459             
26460             var scroll = Roo.select('body', true).first().getScroll();
26461             
26462             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26463                 placement = 'top';
26464             }
26465             
26466             align = this.alignment[placement];
26467         }
26468         
26469         this.el.alignTo(this.bindEl, align[0],align[1]);
26470         //var arrow = this.el.select('.arrow',true).first();
26471         //arrow.set(align[2], 
26472         
26473         this.el.addClass(placement);
26474         
26475         this.el.addClass('in fade');
26476         
26477         this.hoverState = null;
26478         
26479         if (this.el.hasClass('fade')) {
26480             // fade it?
26481         }
26482         
26483     },
26484     hide : function()
26485     {
26486          
26487         if (!this.el) {
26488             return;
26489         }
26490         //this.el.setXY([0,0]);
26491         this.el.removeClass('in');
26492         //this.el.hide();
26493         
26494     }
26495     
26496 });
26497  
26498
26499  /*
26500  * - LGPL
26501  *
26502  * Location Picker
26503  * 
26504  */
26505
26506 /**
26507  * @class Roo.bootstrap.LocationPicker
26508  * @extends Roo.bootstrap.Component
26509  * Bootstrap LocationPicker class
26510  * @cfg {Number} latitude Position when init default 0
26511  * @cfg {Number} longitude Position when init default 0
26512  * @cfg {Number} zoom default 15
26513  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26514  * @cfg {Boolean} mapTypeControl default false
26515  * @cfg {Boolean} disableDoubleClickZoom default false
26516  * @cfg {Boolean} scrollwheel default true
26517  * @cfg {Boolean} streetViewControl default false
26518  * @cfg {Number} radius default 0
26519  * @cfg {String} locationName
26520  * @cfg {Boolean} draggable default true
26521  * @cfg {Boolean} enableAutocomplete default false
26522  * @cfg {Boolean} enableReverseGeocode default true
26523  * @cfg {String} markerTitle
26524  * 
26525  * @constructor
26526  * Create a new LocationPicker
26527  * @param {Object} config The config object
26528  */
26529
26530
26531 Roo.bootstrap.LocationPicker = function(config){
26532     
26533     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26534     
26535     this.addEvents({
26536         /**
26537          * @event initial
26538          * Fires when the picker initialized.
26539          * @param {Roo.bootstrap.LocationPicker} this
26540          * @param {Google Location} location
26541          */
26542         initial : true,
26543         /**
26544          * @event positionchanged
26545          * Fires when the picker position changed.
26546          * @param {Roo.bootstrap.LocationPicker} this
26547          * @param {Google Location} location
26548          */
26549         positionchanged : true,
26550         /**
26551          * @event resize
26552          * Fires when the map resize.
26553          * @param {Roo.bootstrap.LocationPicker} this
26554          */
26555         resize : true,
26556         /**
26557          * @event show
26558          * Fires when the map show.
26559          * @param {Roo.bootstrap.LocationPicker} this
26560          */
26561         show : true,
26562         /**
26563          * @event hide
26564          * Fires when the map hide.
26565          * @param {Roo.bootstrap.LocationPicker} this
26566          */
26567         hide : true,
26568         /**
26569          * @event mapClick
26570          * Fires when click the map.
26571          * @param {Roo.bootstrap.LocationPicker} this
26572          * @param {Map event} e
26573          */
26574         mapClick : true,
26575         /**
26576          * @event mapRightClick
26577          * Fires when right click the map.
26578          * @param {Roo.bootstrap.LocationPicker} this
26579          * @param {Map event} e
26580          */
26581         mapRightClick : true,
26582         /**
26583          * @event markerClick
26584          * Fires when click the marker.
26585          * @param {Roo.bootstrap.LocationPicker} this
26586          * @param {Map event} e
26587          */
26588         markerClick : true,
26589         /**
26590          * @event markerRightClick
26591          * Fires when right click the marker.
26592          * @param {Roo.bootstrap.LocationPicker} this
26593          * @param {Map event} e
26594          */
26595         markerRightClick : true,
26596         /**
26597          * @event OverlayViewDraw
26598          * Fires when OverlayView Draw
26599          * @param {Roo.bootstrap.LocationPicker} this
26600          */
26601         OverlayViewDraw : true,
26602         /**
26603          * @event OverlayViewOnAdd
26604          * Fires when OverlayView Draw
26605          * @param {Roo.bootstrap.LocationPicker} this
26606          */
26607         OverlayViewOnAdd : true,
26608         /**
26609          * @event OverlayViewOnRemove
26610          * Fires when OverlayView Draw
26611          * @param {Roo.bootstrap.LocationPicker} this
26612          */
26613         OverlayViewOnRemove : true,
26614         /**
26615          * @event OverlayViewShow
26616          * Fires when OverlayView Draw
26617          * @param {Roo.bootstrap.LocationPicker} this
26618          * @param {Pixel} cpx
26619          */
26620         OverlayViewShow : true,
26621         /**
26622          * @event OverlayViewHide
26623          * Fires when OverlayView Draw
26624          * @param {Roo.bootstrap.LocationPicker} this
26625          */
26626         OverlayViewHide : true,
26627         /**
26628          * @event loadexception
26629          * Fires when load google lib failed.
26630          * @param {Roo.bootstrap.LocationPicker} this
26631          */
26632         loadexception : true
26633     });
26634         
26635 };
26636
26637 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26638     
26639     gMapContext: false,
26640     
26641     latitude: 0,
26642     longitude: 0,
26643     zoom: 15,
26644     mapTypeId: false,
26645     mapTypeControl: false,
26646     disableDoubleClickZoom: false,
26647     scrollwheel: true,
26648     streetViewControl: false,
26649     radius: 0,
26650     locationName: '',
26651     draggable: true,
26652     enableAutocomplete: false,
26653     enableReverseGeocode: true,
26654     markerTitle: '',
26655     
26656     getAutoCreate: function()
26657     {
26658
26659         var cfg = {
26660             tag: 'div',
26661             cls: 'roo-location-picker'
26662         };
26663         
26664         return cfg
26665     },
26666     
26667     initEvents: function(ct, position)
26668     {       
26669         if(!this.el.getWidth() || this.isApplied()){
26670             return;
26671         }
26672         
26673         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26674         
26675         this.initial();
26676     },
26677     
26678     initial: function()
26679     {
26680         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26681             this.fireEvent('loadexception', this);
26682             return;
26683         }
26684         
26685         if(!this.mapTypeId){
26686             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26687         }
26688         
26689         this.gMapContext = this.GMapContext();
26690         
26691         this.initOverlayView();
26692         
26693         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26694         
26695         var _this = this;
26696                 
26697         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26698             _this.setPosition(_this.gMapContext.marker.position);
26699         });
26700         
26701         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26702             _this.fireEvent('mapClick', this, event);
26703             
26704         });
26705
26706         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26707             _this.fireEvent('mapRightClick', this, event);
26708             
26709         });
26710         
26711         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26712             _this.fireEvent('markerClick', this, event);
26713             
26714         });
26715
26716         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26717             _this.fireEvent('markerRightClick', this, event);
26718             
26719         });
26720         
26721         this.setPosition(this.gMapContext.location);
26722         
26723         this.fireEvent('initial', this, this.gMapContext.location);
26724     },
26725     
26726     initOverlayView: function()
26727     {
26728         var _this = this;
26729         
26730         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26731             
26732             draw: function()
26733             {
26734                 _this.fireEvent('OverlayViewDraw', _this);
26735             },
26736             
26737             onAdd: function()
26738             {
26739                 _this.fireEvent('OverlayViewOnAdd', _this);
26740             },
26741             
26742             onRemove: function()
26743             {
26744                 _this.fireEvent('OverlayViewOnRemove', _this);
26745             },
26746             
26747             show: function(cpx)
26748             {
26749                 _this.fireEvent('OverlayViewShow', _this, cpx);
26750             },
26751             
26752             hide: function()
26753             {
26754                 _this.fireEvent('OverlayViewHide', _this);
26755             }
26756             
26757         });
26758     },
26759     
26760     fromLatLngToContainerPixel: function(event)
26761     {
26762         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26763     },
26764     
26765     isApplied: function() 
26766     {
26767         return this.getGmapContext() == false ? false : true;
26768     },
26769     
26770     getGmapContext: function() 
26771     {
26772         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26773     },
26774     
26775     GMapContext: function() 
26776     {
26777         var position = new google.maps.LatLng(this.latitude, this.longitude);
26778         
26779         var _map = new google.maps.Map(this.el.dom, {
26780             center: position,
26781             zoom: this.zoom,
26782             mapTypeId: this.mapTypeId,
26783             mapTypeControl: this.mapTypeControl,
26784             disableDoubleClickZoom: this.disableDoubleClickZoom,
26785             scrollwheel: this.scrollwheel,
26786             streetViewControl: this.streetViewControl,
26787             locationName: this.locationName,
26788             draggable: this.draggable,
26789             enableAutocomplete: this.enableAutocomplete,
26790             enableReverseGeocode: this.enableReverseGeocode
26791         });
26792         
26793         var _marker = new google.maps.Marker({
26794             position: position,
26795             map: _map,
26796             title: this.markerTitle,
26797             draggable: this.draggable
26798         });
26799         
26800         return {
26801             map: _map,
26802             marker: _marker,
26803             circle: null,
26804             location: position,
26805             radius: this.radius,
26806             locationName: this.locationName,
26807             addressComponents: {
26808                 formatted_address: null,
26809                 addressLine1: null,
26810                 addressLine2: null,
26811                 streetName: null,
26812                 streetNumber: null,
26813                 city: null,
26814                 district: null,
26815                 state: null,
26816                 stateOrProvince: null
26817             },
26818             settings: this,
26819             domContainer: this.el.dom,
26820             geodecoder: new google.maps.Geocoder()
26821         };
26822     },
26823     
26824     drawCircle: function(center, radius, options) 
26825     {
26826         if (this.gMapContext.circle != null) {
26827             this.gMapContext.circle.setMap(null);
26828         }
26829         if (radius > 0) {
26830             radius *= 1;
26831             options = Roo.apply({}, options, {
26832                 strokeColor: "#0000FF",
26833                 strokeOpacity: .35,
26834                 strokeWeight: 2,
26835                 fillColor: "#0000FF",
26836                 fillOpacity: .2
26837             });
26838             
26839             options.map = this.gMapContext.map;
26840             options.radius = radius;
26841             options.center = center;
26842             this.gMapContext.circle = new google.maps.Circle(options);
26843             return this.gMapContext.circle;
26844         }
26845         
26846         return null;
26847     },
26848     
26849     setPosition: function(location) 
26850     {
26851         this.gMapContext.location = location;
26852         this.gMapContext.marker.setPosition(location);
26853         this.gMapContext.map.panTo(location);
26854         this.drawCircle(location, this.gMapContext.radius, {});
26855         
26856         var _this = this;
26857         
26858         if (this.gMapContext.settings.enableReverseGeocode) {
26859             this.gMapContext.geodecoder.geocode({
26860                 latLng: this.gMapContext.location
26861             }, function(results, status) {
26862                 
26863                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26864                     _this.gMapContext.locationName = results[0].formatted_address;
26865                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26866                     
26867                     _this.fireEvent('positionchanged', this, location);
26868                 }
26869             });
26870             
26871             return;
26872         }
26873         
26874         this.fireEvent('positionchanged', this, location);
26875     },
26876     
26877     resize: function()
26878     {
26879         google.maps.event.trigger(this.gMapContext.map, "resize");
26880         
26881         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26882         
26883         this.fireEvent('resize', this);
26884     },
26885     
26886     setPositionByLatLng: function(latitude, longitude)
26887     {
26888         this.setPosition(new google.maps.LatLng(latitude, longitude));
26889     },
26890     
26891     getCurrentPosition: function() 
26892     {
26893         return {
26894             latitude: this.gMapContext.location.lat(),
26895             longitude: this.gMapContext.location.lng()
26896         };
26897     },
26898     
26899     getAddressName: function() 
26900     {
26901         return this.gMapContext.locationName;
26902     },
26903     
26904     getAddressComponents: function() 
26905     {
26906         return this.gMapContext.addressComponents;
26907     },
26908     
26909     address_component_from_google_geocode: function(address_components) 
26910     {
26911         var result = {};
26912         
26913         for (var i = 0; i < address_components.length; i++) {
26914             var component = address_components[i];
26915             if (component.types.indexOf("postal_code") >= 0) {
26916                 result.postalCode = component.short_name;
26917             } else if (component.types.indexOf("street_number") >= 0) {
26918                 result.streetNumber = component.short_name;
26919             } else if (component.types.indexOf("route") >= 0) {
26920                 result.streetName = component.short_name;
26921             } else if (component.types.indexOf("neighborhood") >= 0) {
26922                 result.city = component.short_name;
26923             } else if (component.types.indexOf("locality") >= 0) {
26924                 result.city = component.short_name;
26925             } else if (component.types.indexOf("sublocality") >= 0) {
26926                 result.district = component.short_name;
26927             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26928                 result.stateOrProvince = component.short_name;
26929             } else if (component.types.indexOf("country") >= 0) {
26930                 result.country = component.short_name;
26931             }
26932         }
26933         
26934         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26935         result.addressLine2 = "";
26936         return result;
26937     },
26938     
26939     setZoomLevel: function(zoom)
26940     {
26941         this.gMapContext.map.setZoom(zoom);
26942     },
26943     
26944     show: function()
26945     {
26946         if(!this.el){
26947             return;
26948         }
26949         
26950         this.el.show();
26951         
26952         this.resize();
26953         
26954         this.fireEvent('show', this);
26955     },
26956     
26957     hide: function()
26958     {
26959         if(!this.el){
26960             return;
26961         }
26962         
26963         this.el.hide();
26964         
26965         this.fireEvent('hide', this);
26966     }
26967     
26968 });
26969
26970 Roo.apply(Roo.bootstrap.LocationPicker, {
26971     
26972     OverlayView : function(map, options)
26973     {
26974         options = options || {};
26975         
26976         this.setMap(map);
26977     }
26978     
26979     
26980 });/*
26981  * - LGPL
26982  *
26983  * Alert
26984  * 
26985  */
26986
26987 /**
26988  * @class Roo.bootstrap.Alert
26989  * @extends Roo.bootstrap.Component
26990  * Bootstrap Alert class
26991  * @cfg {String} title The title of alert
26992  * @cfg {String} html The content of alert
26993  * @cfg {String} weight (  success | info | warning | danger )
26994  * @cfg {String} faicon font-awesomeicon
26995  * 
26996  * @constructor
26997  * Create a new alert
26998  * @param {Object} config The config object
26999  */
27000
27001
27002 Roo.bootstrap.Alert = function(config){
27003     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27004     
27005 };
27006
27007 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27008     
27009     title: '',
27010     html: '',
27011     weight: false,
27012     faicon: false,
27013     
27014     getAutoCreate : function()
27015     {
27016         
27017         var cfg = {
27018             tag : 'div',
27019             cls : 'alert',
27020             cn : [
27021                 {
27022                     tag : 'i',
27023                     cls : 'roo-alert-icon'
27024                     
27025                 },
27026                 {
27027                     tag : 'b',
27028                     cls : 'roo-alert-title',
27029                     html : this.title
27030                 },
27031                 {
27032                     tag : 'span',
27033                     cls : 'roo-alert-text',
27034                     html : this.html
27035                 }
27036             ]
27037         };
27038         
27039         if(this.faicon){
27040             cfg.cn[0].cls += ' fa ' + this.faicon;
27041         }
27042         
27043         if(this.weight){
27044             cfg.cls += ' alert-' + this.weight;
27045         }
27046         
27047         return cfg;
27048     },
27049     
27050     initEvents: function() 
27051     {
27052         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27053     },
27054     
27055     setTitle : function(str)
27056     {
27057         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27058     },
27059     
27060     setText : function(str)
27061     {
27062         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27063     },
27064     
27065     setWeight : function(weight)
27066     {
27067         if(this.weight){
27068             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27069         }
27070         
27071         this.weight = weight;
27072         
27073         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27074     },
27075     
27076     setIcon : function(icon)
27077     {
27078         if(this.faicon){
27079             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27080         }
27081         
27082         this.faicon = icon;
27083         
27084         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27085     },
27086     
27087     hide: function() 
27088     {
27089         this.el.hide();   
27090     },
27091     
27092     show: function() 
27093     {  
27094         this.el.show();   
27095     }
27096     
27097 });
27098
27099  
27100 /*
27101 * Licence: LGPL
27102 */
27103
27104 /**
27105  * @class Roo.bootstrap.UploadCropbox
27106  * @extends Roo.bootstrap.Component
27107  * Bootstrap UploadCropbox class
27108  * @cfg {String} emptyText show when image has been loaded
27109  * @cfg {String} rotateNotify show when image too small to rotate
27110  * @cfg {Number} errorTimeout default 3000
27111  * @cfg {Number} minWidth default 300
27112  * @cfg {Number} minHeight default 300
27113  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27114  * @cfg {Boolean} isDocument (true|false) default false
27115  * @cfg {String} url action url
27116  * @cfg {String} paramName default 'imageUpload'
27117  * @cfg {String} method default POST
27118  * @cfg {Boolean} loadMask (true|false) default true
27119  * @cfg {Boolean} loadingText default 'Loading...'
27120  * 
27121  * @constructor
27122  * Create a new UploadCropbox
27123  * @param {Object} config The config object
27124  */
27125
27126 Roo.bootstrap.UploadCropbox = function(config){
27127     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27128     
27129     this.addEvents({
27130         /**
27131          * @event beforeselectfile
27132          * Fire before select file
27133          * @param {Roo.bootstrap.UploadCropbox} this
27134          */
27135         "beforeselectfile" : true,
27136         /**
27137          * @event initial
27138          * Fire after initEvent
27139          * @param {Roo.bootstrap.UploadCropbox} this
27140          */
27141         "initial" : true,
27142         /**
27143          * @event crop
27144          * Fire after initEvent
27145          * @param {Roo.bootstrap.UploadCropbox} this
27146          * @param {String} data
27147          */
27148         "crop" : true,
27149         /**
27150          * @event prepare
27151          * Fire when preparing the file data
27152          * @param {Roo.bootstrap.UploadCropbox} this
27153          * @param {Object} file
27154          */
27155         "prepare" : true,
27156         /**
27157          * @event exception
27158          * Fire when get exception
27159          * @param {Roo.bootstrap.UploadCropbox} this
27160          * @param {XMLHttpRequest} xhr
27161          */
27162         "exception" : true,
27163         /**
27164          * @event beforeloadcanvas
27165          * Fire before load the canvas
27166          * @param {Roo.bootstrap.UploadCropbox} this
27167          * @param {String} src
27168          */
27169         "beforeloadcanvas" : true,
27170         /**
27171          * @event trash
27172          * Fire when trash image
27173          * @param {Roo.bootstrap.UploadCropbox} this
27174          */
27175         "trash" : true,
27176         /**
27177          * @event download
27178          * Fire when download the image
27179          * @param {Roo.bootstrap.UploadCropbox} this
27180          */
27181         "download" : true,
27182         /**
27183          * @event footerbuttonclick
27184          * Fire when footerbuttonclick
27185          * @param {Roo.bootstrap.UploadCropbox} this
27186          * @param {String} type
27187          */
27188         "footerbuttonclick" : true,
27189         /**
27190          * @event resize
27191          * Fire when resize
27192          * @param {Roo.bootstrap.UploadCropbox} this
27193          */
27194         "resize" : true,
27195         /**
27196          * @event rotate
27197          * Fire when rotate the image
27198          * @param {Roo.bootstrap.UploadCropbox} this
27199          * @param {String} pos
27200          */
27201         "rotate" : true,
27202         /**
27203          * @event inspect
27204          * Fire when inspect the file
27205          * @param {Roo.bootstrap.UploadCropbox} this
27206          * @param {Object} file
27207          */
27208         "inspect" : true,
27209         /**
27210          * @event upload
27211          * Fire when xhr upload the file
27212          * @param {Roo.bootstrap.UploadCropbox} this
27213          * @param {Object} data
27214          */
27215         "upload" : true,
27216         /**
27217          * @event arrange
27218          * Fire when arrange the file data
27219          * @param {Roo.bootstrap.UploadCropbox} this
27220          * @param {Object} formData
27221          */
27222         "arrange" : true
27223     });
27224     
27225     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27226 };
27227
27228 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27229     
27230     emptyText : 'Click to upload image',
27231     rotateNotify : 'Image is too small to rotate',
27232     errorTimeout : 3000,
27233     scale : 0,
27234     baseScale : 1,
27235     rotate : 0,
27236     dragable : false,
27237     pinching : false,
27238     mouseX : 0,
27239     mouseY : 0,
27240     cropData : false,
27241     minWidth : 300,
27242     minHeight : 300,
27243     file : false,
27244     exif : {},
27245     baseRotate : 1,
27246     cropType : 'image/jpeg',
27247     buttons : false,
27248     canvasLoaded : false,
27249     isDocument : false,
27250     method : 'POST',
27251     paramName : 'imageUpload',
27252     loadMask : true,
27253     loadingText : 'Loading...',
27254     maskEl : false,
27255     
27256     getAutoCreate : function()
27257     {
27258         var cfg = {
27259             tag : 'div',
27260             cls : 'roo-upload-cropbox',
27261             cn : [
27262                 {
27263                     tag : 'input',
27264                     cls : 'roo-upload-cropbox-selector',
27265                     type : 'file'
27266                 },
27267                 {
27268                     tag : 'div',
27269                     cls : 'roo-upload-cropbox-body',
27270                     style : 'cursor:pointer',
27271                     cn : [
27272                         {
27273                             tag : 'div',
27274                             cls : 'roo-upload-cropbox-preview'
27275                         },
27276                         {
27277                             tag : 'div',
27278                             cls : 'roo-upload-cropbox-thumb'
27279                         },
27280                         {
27281                             tag : 'div',
27282                             cls : 'roo-upload-cropbox-empty-notify',
27283                             html : this.emptyText
27284                         },
27285                         {
27286                             tag : 'div',
27287                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27288                             html : this.rotateNotify
27289                         }
27290                     ]
27291                 },
27292                 {
27293                     tag : 'div',
27294                     cls : 'roo-upload-cropbox-footer',
27295                     cn : {
27296                         tag : 'div',
27297                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27298                         cn : []
27299                     }
27300                 }
27301             ]
27302         };
27303         
27304         return cfg;
27305     },
27306     
27307     onRender : function(ct, position)
27308     {
27309         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27310         
27311         if (this.buttons.length) {
27312             
27313             Roo.each(this.buttons, function(bb) {
27314                 
27315                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27316                 
27317                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27318                 
27319             }, this);
27320         }
27321         
27322         if(this.loadMask){
27323             this.maskEl = this.el;
27324         }
27325     },
27326     
27327     initEvents : function()
27328     {
27329         this.urlAPI = (window.createObjectURL && window) || 
27330                                 (window.URL && URL.revokeObjectURL && URL) || 
27331                                 (window.webkitURL && webkitURL);
27332                         
27333         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27334         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27335         
27336         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27337         this.selectorEl.hide();
27338         
27339         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27340         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27341         
27342         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27343         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27344         this.thumbEl.hide();
27345         
27346         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27347         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27348         
27349         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27350         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27351         this.errorEl.hide();
27352         
27353         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27354         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27355         this.footerEl.hide();
27356         
27357         this.setThumbBoxSize();
27358         
27359         this.bind();
27360         
27361         this.resize();
27362         
27363         this.fireEvent('initial', this);
27364     },
27365
27366     bind : function()
27367     {
27368         var _this = this;
27369         
27370         window.addEventListener("resize", function() { _this.resize(); } );
27371         
27372         this.bodyEl.on('click', this.beforeSelectFile, this);
27373         
27374         if(Roo.isTouch){
27375             this.bodyEl.on('touchstart', this.onTouchStart, this);
27376             this.bodyEl.on('touchmove', this.onTouchMove, this);
27377             this.bodyEl.on('touchend', this.onTouchEnd, this);
27378         }
27379         
27380         if(!Roo.isTouch){
27381             this.bodyEl.on('mousedown', this.onMouseDown, this);
27382             this.bodyEl.on('mousemove', this.onMouseMove, this);
27383             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27384             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27385             Roo.get(document).on('mouseup', this.onMouseUp, this);
27386         }
27387         
27388         this.selectorEl.on('change', this.onFileSelected, this);
27389     },
27390     
27391     reset : function()
27392     {    
27393         this.scale = 0;
27394         this.baseScale = 1;
27395         this.rotate = 0;
27396         this.baseRotate = 1;
27397         this.dragable = false;
27398         this.pinching = false;
27399         this.mouseX = 0;
27400         this.mouseY = 0;
27401         this.cropData = false;
27402         this.notifyEl.dom.innerHTML = this.emptyText;
27403         
27404         this.selectorEl.dom.value = '';
27405         
27406     },
27407     
27408     resize : function()
27409     {
27410         if(this.fireEvent('resize', this) != false){
27411             this.setThumbBoxPosition();
27412             this.setCanvasPosition();
27413         }
27414     },
27415     
27416     onFooterButtonClick : function(e, el, o, type)
27417     {
27418         switch (type) {
27419             case 'rotate-left' :
27420                 this.onRotateLeft(e);
27421                 break;
27422             case 'rotate-right' :
27423                 this.onRotateRight(e);
27424                 break;
27425             case 'picture' :
27426                 this.beforeSelectFile(e);
27427                 break;
27428             case 'trash' :
27429                 this.trash(e);
27430                 break;
27431             case 'crop' :
27432                 this.crop(e);
27433                 break;
27434             case 'download' :
27435                 this.download(e);
27436                 break;
27437             default :
27438                 break;
27439         }
27440         
27441         this.fireEvent('footerbuttonclick', this, type);
27442     },
27443     
27444     beforeSelectFile : function(e)
27445     {
27446         e.preventDefault();
27447         
27448         if(this.fireEvent('beforeselectfile', this) != false){
27449             this.selectorEl.dom.click();
27450         }
27451     },
27452     
27453     onFileSelected : function(e)
27454     {
27455         e.preventDefault();
27456         
27457         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27458             return;
27459         }
27460         
27461         var file = this.selectorEl.dom.files[0];
27462         
27463         if(this.fireEvent('inspect', this, file) != false){
27464             this.prepare(file);
27465         }
27466         
27467     },
27468     
27469     trash : function(e)
27470     {
27471         this.fireEvent('trash', this);
27472     },
27473     
27474     download : function(e)
27475     {
27476         this.fireEvent('download', this);
27477     },
27478     
27479     loadCanvas : function(src)
27480     {   
27481         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27482             
27483             this.reset();
27484             
27485             this.imageEl = document.createElement('img');
27486             
27487             var _this = this;
27488             
27489             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27490             
27491             this.imageEl.src = src;
27492         }
27493     },
27494     
27495     onLoadCanvas : function()
27496     {   
27497         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27498         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27499         
27500         this.bodyEl.un('click', this.beforeSelectFile, this);
27501         
27502         this.notifyEl.hide();
27503         this.thumbEl.show();
27504         this.footerEl.show();
27505         
27506         this.baseRotateLevel();
27507         
27508         if(this.isDocument){
27509             this.setThumbBoxSize();
27510         }
27511         
27512         this.setThumbBoxPosition();
27513         
27514         this.baseScaleLevel();
27515         
27516         this.draw();
27517         
27518         this.resize();
27519         
27520         this.canvasLoaded = true;
27521         
27522         if(this.loadMask){
27523             this.maskEl.unmask();
27524         }
27525         
27526     },
27527     
27528     setCanvasPosition : function()
27529     {   
27530         if(!this.canvasEl){
27531             return;
27532         }
27533         
27534         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27535         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27536         
27537         this.previewEl.setLeft(pw);
27538         this.previewEl.setTop(ph);
27539         
27540     },
27541     
27542     onMouseDown : function(e)
27543     {   
27544         e.stopEvent();
27545         
27546         this.dragable = true;
27547         this.pinching = false;
27548         
27549         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27550             this.dragable = false;
27551             return;
27552         }
27553         
27554         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27555         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27556         
27557     },
27558     
27559     onMouseMove : function(e)
27560     {   
27561         e.stopEvent();
27562         
27563         if(!this.canvasLoaded){
27564             return;
27565         }
27566         
27567         if (!this.dragable){
27568             return;
27569         }
27570         
27571         var minX = Math.ceil(this.thumbEl.getLeft(true));
27572         var minY = Math.ceil(this.thumbEl.getTop(true));
27573         
27574         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27575         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27576         
27577         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27578         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27579         
27580         x = x - this.mouseX;
27581         y = y - this.mouseY;
27582         
27583         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27584         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27585         
27586         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27587         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27588         
27589         this.previewEl.setLeft(bgX);
27590         this.previewEl.setTop(bgY);
27591         
27592         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27593         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27594     },
27595     
27596     onMouseUp : function(e)
27597     {   
27598         e.stopEvent();
27599         
27600         this.dragable = false;
27601     },
27602     
27603     onMouseWheel : function(e)
27604     {   
27605         e.stopEvent();
27606         
27607         this.startScale = this.scale;
27608         
27609         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27610         
27611         if(!this.zoomable()){
27612             this.scale = this.startScale;
27613             return;
27614         }
27615         
27616         this.draw();
27617         
27618         return;
27619     },
27620     
27621     zoomable : function()
27622     {
27623         var minScale = this.thumbEl.getWidth() / this.minWidth;
27624         
27625         if(this.minWidth < this.minHeight){
27626             minScale = this.thumbEl.getHeight() / this.minHeight;
27627         }
27628         
27629         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27630         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27631         
27632         if(
27633                 this.isDocument &&
27634                 (this.rotate == 0 || this.rotate == 180) && 
27635                 (
27636                     width > this.imageEl.OriginWidth || 
27637                     height > this.imageEl.OriginHeight ||
27638                     (width < this.minWidth && height < this.minHeight)
27639                 )
27640         ){
27641             return false;
27642         }
27643         
27644         if(
27645                 this.isDocument &&
27646                 (this.rotate == 90 || this.rotate == 270) && 
27647                 (
27648                     width > this.imageEl.OriginWidth || 
27649                     height > this.imageEl.OriginHeight ||
27650                     (width < this.minHeight && height < this.minWidth)
27651                 )
27652         ){
27653             return false;
27654         }
27655         
27656         if(
27657                 !this.isDocument &&
27658                 (this.rotate == 0 || this.rotate == 180) && 
27659                 (
27660                     width < this.minWidth || 
27661                     width > this.imageEl.OriginWidth || 
27662                     height < this.minHeight || 
27663                     height > this.imageEl.OriginHeight
27664                 )
27665         ){
27666             return false;
27667         }
27668         
27669         if(
27670                 !this.isDocument &&
27671                 (this.rotate == 90 || this.rotate == 270) && 
27672                 (
27673                     width < this.minHeight || 
27674                     width > this.imageEl.OriginWidth || 
27675                     height < this.minWidth || 
27676                     height > this.imageEl.OriginHeight
27677                 )
27678         ){
27679             return false;
27680         }
27681         
27682         return true;
27683         
27684     },
27685     
27686     onRotateLeft : function(e)
27687     {   
27688         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27689             
27690             var minScale = this.thumbEl.getWidth() / this.minWidth;
27691             
27692             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27693             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27694             
27695             this.startScale = this.scale;
27696             
27697             while (this.getScaleLevel() < minScale){
27698             
27699                 this.scale = this.scale + 1;
27700                 
27701                 if(!this.zoomable()){
27702                     break;
27703                 }
27704                 
27705                 if(
27706                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27707                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27708                 ){
27709                     continue;
27710                 }
27711                 
27712                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27713
27714                 this.draw();
27715                 
27716                 return;
27717             }
27718             
27719             this.scale = this.startScale;
27720             
27721             this.onRotateFail();
27722             
27723             return false;
27724         }
27725         
27726         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27727
27728         if(this.isDocument){
27729             this.setThumbBoxSize();
27730             this.setThumbBoxPosition();
27731             this.setCanvasPosition();
27732         }
27733         
27734         this.draw();
27735         
27736         this.fireEvent('rotate', this, 'left');
27737         
27738     },
27739     
27740     onRotateRight : function(e)
27741     {
27742         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27743             
27744             var minScale = this.thumbEl.getWidth() / this.minWidth;
27745         
27746             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27747             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27748             
27749             this.startScale = this.scale;
27750             
27751             while (this.getScaleLevel() < minScale){
27752             
27753                 this.scale = this.scale + 1;
27754                 
27755                 if(!this.zoomable()){
27756                     break;
27757                 }
27758                 
27759                 if(
27760                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27761                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27762                 ){
27763                     continue;
27764                 }
27765                 
27766                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27767
27768                 this.draw();
27769                 
27770                 return;
27771             }
27772             
27773             this.scale = this.startScale;
27774             
27775             this.onRotateFail();
27776             
27777             return false;
27778         }
27779         
27780         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27781
27782         if(this.isDocument){
27783             this.setThumbBoxSize();
27784             this.setThumbBoxPosition();
27785             this.setCanvasPosition();
27786         }
27787         
27788         this.draw();
27789         
27790         this.fireEvent('rotate', this, 'right');
27791     },
27792     
27793     onRotateFail : function()
27794     {
27795         this.errorEl.show(true);
27796         
27797         var _this = this;
27798         
27799         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27800     },
27801     
27802     draw : function()
27803     {
27804         this.previewEl.dom.innerHTML = '';
27805         
27806         var canvasEl = document.createElement("canvas");
27807         
27808         var contextEl = canvasEl.getContext("2d");
27809         
27810         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27811         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27812         var center = this.imageEl.OriginWidth / 2;
27813         
27814         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27815             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27816             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27817             center = this.imageEl.OriginHeight / 2;
27818         }
27819         
27820         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27821         
27822         contextEl.translate(center, center);
27823         contextEl.rotate(this.rotate * Math.PI / 180);
27824
27825         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27826         
27827         this.canvasEl = document.createElement("canvas");
27828         
27829         this.contextEl = this.canvasEl.getContext("2d");
27830         
27831         switch (this.rotate) {
27832             case 0 :
27833                 
27834                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27835                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27836                 
27837                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27838                 
27839                 break;
27840             case 90 : 
27841                 
27842                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27843                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27844                 
27845                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27846                     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);
27847                     break;
27848                 }
27849                 
27850                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27851                 
27852                 break;
27853             case 180 :
27854                 
27855                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27856                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27857                 
27858                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27859                     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);
27860                     break;
27861                 }
27862                 
27863                 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);
27864                 
27865                 break;
27866             case 270 :
27867                 
27868                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27869                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27870         
27871                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27872                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27873                     break;
27874                 }
27875                 
27876                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27877                 
27878                 break;
27879             default : 
27880                 break;
27881         }
27882         
27883         this.previewEl.appendChild(this.canvasEl);
27884         
27885         this.setCanvasPosition();
27886     },
27887     
27888     crop : function()
27889     {
27890         if(!this.canvasLoaded){
27891             return;
27892         }
27893         
27894         var imageCanvas = document.createElement("canvas");
27895         
27896         var imageContext = imageCanvas.getContext("2d");
27897         
27898         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27899         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27900         
27901         var center = imageCanvas.width / 2;
27902         
27903         imageContext.translate(center, center);
27904         
27905         imageContext.rotate(this.rotate * Math.PI / 180);
27906         
27907         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27908         
27909         var canvas = document.createElement("canvas");
27910         
27911         var context = canvas.getContext("2d");
27912                 
27913         canvas.width = this.minWidth;
27914         canvas.height = this.minHeight;
27915
27916         switch (this.rotate) {
27917             case 0 :
27918                 
27919                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27920                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27921                 
27922                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27923                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27924                 
27925                 var targetWidth = this.minWidth - 2 * x;
27926                 var targetHeight = this.minHeight - 2 * y;
27927                 
27928                 var scale = 1;
27929                 
27930                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27931                     scale = targetWidth / width;
27932                 }
27933                 
27934                 if(x > 0 && y == 0){
27935                     scale = targetHeight / height;
27936                 }
27937                 
27938                 if(x > 0 && y > 0){
27939                     scale = targetWidth / width;
27940                     
27941                     if(width < height){
27942                         scale = targetHeight / height;
27943                     }
27944                 }
27945                 
27946                 context.scale(scale, scale);
27947                 
27948                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27949                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27950
27951                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27952                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27953
27954                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27955                 
27956                 break;
27957             case 90 : 
27958                 
27959                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27960                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27961                 
27962                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27963                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27964                 
27965                 var targetWidth = this.minWidth - 2 * x;
27966                 var targetHeight = this.minHeight - 2 * y;
27967                 
27968                 var scale = 1;
27969                 
27970                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27971                     scale = targetWidth / width;
27972                 }
27973                 
27974                 if(x > 0 && y == 0){
27975                     scale = targetHeight / height;
27976                 }
27977                 
27978                 if(x > 0 && y > 0){
27979                     scale = targetWidth / width;
27980                     
27981                     if(width < height){
27982                         scale = targetHeight / height;
27983                     }
27984                 }
27985                 
27986                 context.scale(scale, scale);
27987                 
27988                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27989                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27990
27991                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27992                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27993                 
27994                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27995                 
27996                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27997                 
27998                 break;
27999             case 180 :
28000                 
28001                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28002                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28003                 
28004                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28005                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28006                 
28007                 var targetWidth = this.minWidth - 2 * x;
28008                 var targetHeight = this.minHeight - 2 * y;
28009                 
28010                 var scale = 1;
28011                 
28012                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28013                     scale = targetWidth / width;
28014                 }
28015                 
28016                 if(x > 0 && y == 0){
28017                     scale = targetHeight / height;
28018                 }
28019                 
28020                 if(x > 0 && y > 0){
28021                     scale = targetWidth / width;
28022                     
28023                     if(width < height){
28024                         scale = targetHeight / height;
28025                     }
28026                 }
28027                 
28028                 context.scale(scale, scale);
28029                 
28030                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28031                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28032
28033                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28034                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28035
28036                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28037                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28038                 
28039                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28040                 
28041                 break;
28042             case 270 :
28043                 
28044                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28045                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28046                 
28047                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28048                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28049                 
28050                 var targetWidth = this.minWidth - 2 * x;
28051                 var targetHeight = this.minHeight - 2 * y;
28052                 
28053                 var scale = 1;
28054                 
28055                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28056                     scale = targetWidth / width;
28057                 }
28058                 
28059                 if(x > 0 && y == 0){
28060                     scale = targetHeight / height;
28061                 }
28062                 
28063                 if(x > 0 && y > 0){
28064                     scale = targetWidth / width;
28065                     
28066                     if(width < height){
28067                         scale = targetHeight / height;
28068                     }
28069                 }
28070                 
28071                 context.scale(scale, scale);
28072                 
28073                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28074                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28075
28076                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28077                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28078                 
28079                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28080                 
28081                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28082                 
28083                 break;
28084             default : 
28085                 break;
28086         }
28087         
28088         this.cropData = canvas.toDataURL(this.cropType);
28089         
28090         if(this.fireEvent('crop', this, this.cropData) !== false){
28091             this.process(this.file, this.cropData);
28092         }
28093         
28094         return;
28095         
28096     },
28097     
28098     setThumbBoxSize : function()
28099     {
28100         var width, height;
28101         
28102         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28103             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28104             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28105             
28106             this.minWidth = width;
28107             this.minHeight = height;
28108             
28109             if(this.rotate == 90 || this.rotate == 270){
28110                 this.minWidth = height;
28111                 this.minHeight = width;
28112             }
28113         }
28114         
28115         height = 300;
28116         width = Math.ceil(this.minWidth * height / this.minHeight);
28117         
28118         if(this.minWidth > this.minHeight){
28119             width = 300;
28120             height = Math.ceil(this.minHeight * width / this.minWidth);
28121         }
28122         
28123         this.thumbEl.setStyle({
28124             width : width + 'px',
28125             height : height + 'px'
28126         });
28127
28128         return;
28129             
28130     },
28131     
28132     setThumbBoxPosition : function()
28133     {
28134         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28135         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28136         
28137         this.thumbEl.setLeft(x);
28138         this.thumbEl.setTop(y);
28139         
28140     },
28141     
28142     baseRotateLevel : function()
28143     {
28144         this.baseRotate = 1;
28145         
28146         if(
28147                 typeof(this.exif) != 'undefined' &&
28148                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28149                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28150         ){
28151             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28152         }
28153         
28154         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28155         
28156     },
28157     
28158     baseScaleLevel : function()
28159     {
28160         var width, height;
28161         
28162         if(this.isDocument){
28163             
28164             if(this.baseRotate == 6 || this.baseRotate == 8){
28165             
28166                 height = this.thumbEl.getHeight();
28167                 this.baseScale = height / this.imageEl.OriginWidth;
28168
28169                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28170                     width = this.thumbEl.getWidth();
28171                     this.baseScale = width / this.imageEl.OriginHeight;
28172                 }
28173
28174                 return;
28175             }
28176
28177             height = this.thumbEl.getHeight();
28178             this.baseScale = height / this.imageEl.OriginHeight;
28179
28180             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28181                 width = this.thumbEl.getWidth();
28182                 this.baseScale = width / this.imageEl.OriginWidth;
28183             }
28184
28185             return;
28186         }
28187         
28188         if(this.baseRotate == 6 || this.baseRotate == 8){
28189             
28190             width = this.thumbEl.getHeight();
28191             this.baseScale = width / this.imageEl.OriginHeight;
28192             
28193             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28194                 height = this.thumbEl.getWidth();
28195                 this.baseScale = height / this.imageEl.OriginHeight;
28196             }
28197             
28198             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28199                 height = this.thumbEl.getWidth();
28200                 this.baseScale = height / this.imageEl.OriginHeight;
28201                 
28202                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28203                     width = this.thumbEl.getHeight();
28204                     this.baseScale = width / this.imageEl.OriginWidth;
28205                 }
28206             }
28207             
28208             return;
28209         }
28210         
28211         width = this.thumbEl.getWidth();
28212         this.baseScale = width / this.imageEl.OriginWidth;
28213         
28214         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28215             height = this.thumbEl.getHeight();
28216             this.baseScale = height / this.imageEl.OriginHeight;
28217         }
28218         
28219         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28220             
28221             height = this.thumbEl.getHeight();
28222             this.baseScale = height / this.imageEl.OriginHeight;
28223             
28224             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28225                 width = this.thumbEl.getWidth();
28226                 this.baseScale = width / this.imageEl.OriginWidth;
28227             }
28228             
28229         }
28230         
28231         return;
28232     },
28233     
28234     getScaleLevel : function()
28235     {
28236         return this.baseScale * Math.pow(1.1, this.scale);
28237     },
28238     
28239     onTouchStart : function(e)
28240     {
28241         if(!this.canvasLoaded){
28242             this.beforeSelectFile(e);
28243             return;
28244         }
28245         
28246         var touches = e.browserEvent.touches;
28247         
28248         if(!touches){
28249             return;
28250         }
28251         
28252         if(touches.length == 1){
28253             this.onMouseDown(e);
28254             return;
28255         }
28256         
28257         if(touches.length != 2){
28258             return;
28259         }
28260         
28261         var coords = [];
28262         
28263         for(var i = 0, finger; finger = touches[i]; i++){
28264             coords.push(finger.pageX, finger.pageY);
28265         }
28266         
28267         var x = Math.pow(coords[0] - coords[2], 2);
28268         var y = Math.pow(coords[1] - coords[3], 2);
28269         
28270         this.startDistance = Math.sqrt(x + y);
28271         
28272         this.startScale = this.scale;
28273         
28274         this.pinching = true;
28275         this.dragable = false;
28276         
28277     },
28278     
28279     onTouchMove : function(e)
28280     {
28281         if(!this.pinching && !this.dragable){
28282             return;
28283         }
28284         
28285         var touches = e.browserEvent.touches;
28286         
28287         if(!touches){
28288             return;
28289         }
28290         
28291         if(this.dragable){
28292             this.onMouseMove(e);
28293             return;
28294         }
28295         
28296         var coords = [];
28297         
28298         for(var i = 0, finger; finger = touches[i]; i++){
28299             coords.push(finger.pageX, finger.pageY);
28300         }
28301         
28302         var x = Math.pow(coords[0] - coords[2], 2);
28303         var y = Math.pow(coords[1] - coords[3], 2);
28304         
28305         this.endDistance = Math.sqrt(x + y);
28306         
28307         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28308         
28309         if(!this.zoomable()){
28310             this.scale = this.startScale;
28311             return;
28312         }
28313         
28314         this.draw();
28315         
28316     },
28317     
28318     onTouchEnd : function(e)
28319     {
28320         this.pinching = false;
28321         this.dragable = false;
28322         
28323     },
28324     
28325     process : function(file, crop)
28326     {
28327         if(this.loadMask){
28328             this.maskEl.mask(this.loadingText);
28329         }
28330         
28331         this.xhr = new XMLHttpRequest();
28332         
28333         file.xhr = this.xhr;
28334
28335         this.xhr.open(this.method, this.url, true);
28336         
28337         var headers = {
28338             "Accept": "application/json",
28339             "Cache-Control": "no-cache",
28340             "X-Requested-With": "XMLHttpRequest"
28341         };
28342         
28343         for (var headerName in headers) {
28344             var headerValue = headers[headerName];
28345             if (headerValue) {
28346                 this.xhr.setRequestHeader(headerName, headerValue);
28347             }
28348         }
28349         
28350         var _this = this;
28351         
28352         this.xhr.onload = function()
28353         {
28354             _this.xhrOnLoad(_this.xhr);
28355         }
28356         
28357         this.xhr.onerror = function()
28358         {
28359             _this.xhrOnError(_this.xhr);
28360         }
28361         
28362         var formData = new FormData();
28363
28364         formData.append('returnHTML', 'NO');
28365         
28366         if(crop){
28367             formData.append('crop', crop);
28368         }
28369         
28370         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28371             formData.append(this.paramName, file, file.name);
28372         }
28373         
28374         if(typeof(file.filename) != 'undefined'){
28375             formData.append('filename', file.filename);
28376         }
28377         
28378         if(typeof(file.mimetype) != 'undefined'){
28379             formData.append('mimetype', file.mimetype);
28380         }
28381         
28382         if(this.fireEvent('arrange', this, formData) != false){
28383             this.xhr.send(formData);
28384         };
28385     },
28386     
28387     xhrOnLoad : function(xhr)
28388     {
28389         if(this.loadMask){
28390             this.maskEl.unmask();
28391         }
28392         
28393         if (xhr.readyState !== 4) {
28394             this.fireEvent('exception', this, xhr);
28395             return;
28396         }
28397
28398         var response = Roo.decode(xhr.responseText);
28399         
28400         if(!response.success){
28401             this.fireEvent('exception', this, xhr);
28402             return;
28403         }
28404         
28405         var response = Roo.decode(xhr.responseText);
28406         
28407         this.fireEvent('upload', this, response);
28408         
28409     },
28410     
28411     xhrOnError : function()
28412     {
28413         if(this.loadMask){
28414             this.maskEl.unmask();
28415         }
28416         
28417         Roo.log('xhr on error');
28418         
28419         var response = Roo.decode(xhr.responseText);
28420           
28421         Roo.log(response);
28422         
28423     },
28424     
28425     prepare : function(file)
28426     {   
28427         if(this.loadMask){
28428             this.maskEl.mask(this.loadingText);
28429         }
28430         
28431         this.file = false;
28432         this.exif = {};
28433         
28434         if(typeof(file) === 'string'){
28435             this.loadCanvas(file);
28436             return;
28437         }
28438         
28439         if(!file || !this.urlAPI){
28440             return;
28441         }
28442         
28443         this.file = file;
28444         this.cropType = file.type;
28445         
28446         var _this = this;
28447         
28448         if(this.fireEvent('prepare', this, this.file) != false){
28449             
28450             var reader = new FileReader();
28451             
28452             reader.onload = function (e) {
28453                 if (e.target.error) {
28454                     Roo.log(e.target.error);
28455                     return;
28456                 }
28457                 
28458                 var buffer = e.target.result,
28459                     dataView = new DataView(buffer),
28460                     offset = 2,
28461                     maxOffset = dataView.byteLength - 4,
28462                     markerBytes,
28463                     markerLength;
28464                 
28465                 if (dataView.getUint16(0) === 0xffd8) {
28466                     while (offset < maxOffset) {
28467                         markerBytes = dataView.getUint16(offset);
28468                         
28469                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28470                             markerLength = dataView.getUint16(offset + 2) + 2;
28471                             if (offset + markerLength > dataView.byteLength) {
28472                                 Roo.log('Invalid meta data: Invalid segment size.');
28473                                 break;
28474                             }
28475                             
28476                             if(markerBytes == 0xffe1){
28477                                 _this.parseExifData(
28478                                     dataView,
28479                                     offset,
28480                                     markerLength
28481                                 );
28482                             }
28483                             
28484                             offset += markerLength;
28485                             
28486                             continue;
28487                         }
28488                         
28489                         break;
28490                     }
28491                     
28492                 }
28493                 
28494                 var url = _this.urlAPI.createObjectURL(_this.file);
28495                 
28496                 _this.loadCanvas(url);
28497                 
28498                 return;
28499             }
28500             
28501             reader.readAsArrayBuffer(this.file);
28502             
28503         }
28504         
28505     },
28506     
28507     parseExifData : function(dataView, offset, length)
28508     {
28509         var tiffOffset = offset + 10,
28510             littleEndian,
28511             dirOffset;
28512     
28513         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28514             // No Exif data, might be XMP data instead
28515             return;
28516         }
28517         
28518         // Check for the ASCII code for "Exif" (0x45786966):
28519         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28520             // No Exif data, might be XMP data instead
28521             return;
28522         }
28523         if (tiffOffset + 8 > dataView.byteLength) {
28524             Roo.log('Invalid Exif data: Invalid segment size.');
28525             return;
28526         }
28527         // Check for the two null bytes:
28528         if (dataView.getUint16(offset + 8) !== 0x0000) {
28529             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28530             return;
28531         }
28532         // Check the byte alignment:
28533         switch (dataView.getUint16(tiffOffset)) {
28534         case 0x4949:
28535             littleEndian = true;
28536             break;
28537         case 0x4D4D:
28538             littleEndian = false;
28539             break;
28540         default:
28541             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28542             return;
28543         }
28544         // Check for the TIFF tag marker (0x002A):
28545         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28546             Roo.log('Invalid Exif data: Missing TIFF marker.');
28547             return;
28548         }
28549         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28550         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28551         
28552         this.parseExifTags(
28553             dataView,
28554             tiffOffset,
28555             tiffOffset + dirOffset,
28556             littleEndian
28557         );
28558     },
28559     
28560     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28561     {
28562         var tagsNumber,
28563             dirEndOffset,
28564             i;
28565         if (dirOffset + 6 > dataView.byteLength) {
28566             Roo.log('Invalid Exif data: Invalid directory offset.');
28567             return;
28568         }
28569         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28570         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28571         if (dirEndOffset + 4 > dataView.byteLength) {
28572             Roo.log('Invalid Exif data: Invalid directory size.');
28573             return;
28574         }
28575         for (i = 0; i < tagsNumber; i += 1) {
28576             this.parseExifTag(
28577                 dataView,
28578                 tiffOffset,
28579                 dirOffset + 2 + 12 * i, // tag offset
28580                 littleEndian
28581             );
28582         }
28583         // Return the offset to the next directory:
28584         return dataView.getUint32(dirEndOffset, littleEndian);
28585     },
28586     
28587     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28588     {
28589         var tag = dataView.getUint16(offset, littleEndian);
28590         
28591         this.exif[tag] = this.getExifValue(
28592             dataView,
28593             tiffOffset,
28594             offset,
28595             dataView.getUint16(offset + 2, littleEndian), // tag type
28596             dataView.getUint32(offset + 4, littleEndian), // tag length
28597             littleEndian
28598         );
28599     },
28600     
28601     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28602     {
28603         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28604             tagSize,
28605             dataOffset,
28606             values,
28607             i,
28608             str,
28609             c;
28610     
28611         if (!tagType) {
28612             Roo.log('Invalid Exif data: Invalid tag type.');
28613             return;
28614         }
28615         
28616         tagSize = tagType.size * length;
28617         // Determine if the value is contained in the dataOffset bytes,
28618         // or if the value at the dataOffset is a pointer to the actual data:
28619         dataOffset = tagSize > 4 ?
28620                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28621         if (dataOffset + tagSize > dataView.byteLength) {
28622             Roo.log('Invalid Exif data: Invalid data offset.');
28623             return;
28624         }
28625         if (length === 1) {
28626             return tagType.getValue(dataView, dataOffset, littleEndian);
28627         }
28628         values = [];
28629         for (i = 0; i < length; i += 1) {
28630             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28631         }
28632         
28633         if (tagType.ascii) {
28634             str = '';
28635             // Concatenate the chars:
28636             for (i = 0; i < values.length; i += 1) {
28637                 c = values[i];
28638                 // Ignore the terminating NULL byte(s):
28639                 if (c === '\u0000') {
28640                     break;
28641                 }
28642                 str += c;
28643             }
28644             return str;
28645         }
28646         return values;
28647     }
28648     
28649 });
28650
28651 Roo.apply(Roo.bootstrap.UploadCropbox, {
28652     tags : {
28653         'Orientation': 0x0112
28654     },
28655     
28656     Orientation: {
28657             1: 0, //'top-left',
28658 //            2: 'top-right',
28659             3: 180, //'bottom-right',
28660 //            4: 'bottom-left',
28661 //            5: 'left-top',
28662             6: 90, //'right-top',
28663 //            7: 'right-bottom',
28664             8: 270 //'left-bottom'
28665     },
28666     
28667     exifTagTypes : {
28668         // byte, 8-bit unsigned int:
28669         1: {
28670             getValue: function (dataView, dataOffset) {
28671                 return dataView.getUint8(dataOffset);
28672             },
28673             size: 1
28674         },
28675         // ascii, 8-bit byte:
28676         2: {
28677             getValue: function (dataView, dataOffset) {
28678                 return String.fromCharCode(dataView.getUint8(dataOffset));
28679             },
28680             size: 1,
28681             ascii: true
28682         },
28683         // short, 16 bit int:
28684         3: {
28685             getValue: function (dataView, dataOffset, littleEndian) {
28686                 return dataView.getUint16(dataOffset, littleEndian);
28687             },
28688             size: 2
28689         },
28690         // long, 32 bit int:
28691         4: {
28692             getValue: function (dataView, dataOffset, littleEndian) {
28693                 return dataView.getUint32(dataOffset, littleEndian);
28694             },
28695             size: 4
28696         },
28697         // rational = two long values, first is numerator, second is denominator:
28698         5: {
28699             getValue: function (dataView, dataOffset, littleEndian) {
28700                 return dataView.getUint32(dataOffset, littleEndian) /
28701                     dataView.getUint32(dataOffset + 4, littleEndian);
28702             },
28703             size: 8
28704         },
28705         // slong, 32 bit signed int:
28706         9: {
28707             getValue: function (dataView, dataOffset, littleEndian) {
28708                 return dataView.getInt32(dataOffset, littleEndian);
28709             },
28710             size: 4
28711         },
28712         // srational, two slongs, first is numerator, second is denominator:
28713         10: {
28714             getValue: function (dataView, dataOffset, littleEndian) {
28715                 return dataView.getInt32(dataOffset, littleEndian) /
28716                     dataView.getInt32(dataOffset + 4, littleEndian);
28717             },
28718             size: 8
28719         }
28720     },
28721     
28722     footer : {
28723         STANDARD : [
28724             {
28725                 tag : 'div',
28726                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28727                 action : 'rotate-left',
28728                 cn : [
28729                     {
28730                         tag : 'button',
28731                         cls : 'btn btn-default',
28732                         html : '<i class="fa fa-undo"></i>'
28733                     }
28734                 ]
28735             },
28736             {
28737                 tag : 'div',
28738                 cls : 'btn-group roo-upload-cropbox-picture',
28739                 action : 'picture',
28740                 cn : [
28741                     {
28742                         tag : 'button',
28743                         cls : 'btn btn-default',
28744                         html : '<i class="fa fa-picture-o"></i>'
28745                     }
28746                 ]
28747             },
28748             {
28749                 tag : 'div',
28750                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28751                 action : 'rotate-right',
28752                 cn : [
28753                     {
28754                         tag : 'button',
28755                         cls : 'btn btn-default',
28756                         html : '<i class="fa fa-repeat"></i>'
28757                     }
28758                 ]
28759             }
28760         ],
28761         DOCUMENT : [
28762             {
28763                 tag : 'div',
28764                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28765                 action : 'rotate-left',
28766                 cn : [
28767                     {
28768                         tag : 'button',
28769                         cls : 'btn btn-default',
28770                         html : '<i class="fa fa-undo"></i>'
28771                     }
28772                 ]
28773             },
28774             {
28775                 tag : 'div',
28776                 cls : 'btn-group roo-upload-cropbox-download',
28777                 action : 'download',
28778                 cn : [
28779                     {
28780                         tag : 'button',
28781                         cls : 'btn btn-default',
28782                         html : '<i class="fa fa-download"></i>'
28783                     }
28784                 ]
28785             },
28786             {
28787                 tag : 'div',
28788                 cls : 'btn-group roo-upload-cropbox-crop',
28789                 action : 'crop',
28790                 cn : [
28791                     {
28792                         tag : 'button',
28793                         cls : 'btn btn-default',
28794                         html : '<i class="fa fa-crop"></i>'
28795                     }
28796                 ]
28797             },
28798             {
28799                 tag : 'div',
28800                 cls : 'btn-group roo-upload-cropbox-trash',
28801                 action : 'trash',
28802                 cn : [
28803                     {
28804                         tag : 'button',
28805                         cls : 'btn btn-default',
28806                         html : '<i class="fa fa-trash"></i>'
28807                     }
28808                 ]
28809             },
28810             {
28811                 tag : 'div',
28812                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28813                 action : 'rotate-right',
28814                 cn : [
28815                     {
28816                         tag : 'button',
28817                         cls : 'btn btn-default',
28818                         html : '<i class="fa fa-repeat"></i>'
28819                     }
28820                 ]
28821             }
28822         ],
28823         ROTATOR : [
28824             {
28825                 tag : 'div',
28826                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28827                 action : 'rotate-left',
28828                 cn : [
28829                     {
28830                         tag : 'button',
28831                         cls : 'btn btn-default',
28832                         html : '<i class="fa fa-undo"></i>'
28833                     }
28834                 ]
28835             },
28836             {
28837                 tag : 'div',
28838                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28839                 action : 'rotate-right',
28840                 cn : [
28841                     {
28842                         tag : 'button',
28843                         cls : 'btn btn-default',
28844                         html : '<i class="fa fa-repeat"></i>'
28845                     }
28846                 ]
28847             }
28848         ]
28849     }
28850 });
28851
28852 /*
28853 * Licence: LGPL
28854 */
28855
28856 /**
28857  * @class Roo.bootstrap.DocumentManager
28858  * @extends Roo.bootstrap.Component
28859  * Bootstrap DocumentManager class
28860  * @cfg {String} paramName default 'imageUpload'
28861  * @cfg {String} toolTipName default 'filename'
28862  * @cfg {String} method default POST
28863  * @cfg {String} url action url
28864  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28865  * @cfg {Boolean} multiple multiple upload default true
28866  * @cfg {Number} thumbSize default 300
28867  * @cfg {String} fieldLabel
28868  * @cfg {Number} labelWidth default 4
28869  * @cfg {String} labelAlign (left|top) default left
28870  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28871 * @cfg {Number} labellg set the width of label (1-12)
28872  * @cfg {Number} labelmd set the width of label (1-12)
28873  * @cfg {Number} labelsm set the width of label (1-12)
28874  * @cfg {Number} labelxs set the width of label (1-12)
28875  * 
28876  * @constructor
28877  * Create a new DocumentManager
28878  * @param {Object} config The config object
28879  */
28880
28881 Roo.bootstrap.DocumentManager = function(config){
28882     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28883     
28884     this.files = [];
28885     this.delegates = [];
28886     
28887     this.addEvents({
28888         /**
28889          * @event initial
28890          * Fire when initial the DocumentManager
28891          * @param {Roo.bootstrap.DocumentManager} this
28892          */
28893         "initial" : true,
28894         /**
28895          * @event inspect
28896          * inspect selected file
28897          * @param {Roo.bootstrap.DocumentManager} this
28898          * @param {File} file
28899          */
28900         "inspect" : true,
28901         /**
28902          * @event exception
28903          * Fire when xhr load exception
28904          * @param {Roo.bootstrap.DocumentManager} this
28905          * @param {XMLHttpRequest} xhr
28906          */
28907         "exception" : true,
28908         /**
28909          * @event afterupload
28910          * Fire when xhr load exception
28911          * @param {Roo.bootstrap.DocumentManager} this
28912          * @param {XMLHttpRequest} xhr
28913          */
28914         "afterupload" : true,
28915         /**
28916          * @event prepare
28917          * prepare the form data
28918          * @param {Roo.bootstrap.DocumentManager} this
28919          * @param {Object} formData
28920          */
28921         "prepare" : true,
28922         /**
28923          * @event remove
28924          * Fire when remove the file
28925          * @param {Roo.bootstrap.DocumentManager} this
28926          * @param {Object} file
28927          */
28928         "remove" : true,
28929         /**
28930          * @event refresh
28931          * Fire after refresh the file
28932          * @param {Roo.bootstrap.DocumentManager} this
28933          */
28934         "refresh" : true,
28935         /**
28936          * @event click
28937          * Fire after click the image
28938          * @param {Roo.bootstrap.DocumentManager} this
28939          * @param {Object} file
28940          */
28941         "click" : true,
28942         /**
28943          * @event edit
28944          * Fire when upload a image and editable set to true
28945          * @param {Roo.bootstrap.DocumentManager} this
28946          * @param {Object} file
28947          */
28948         "edit" : true,
28949         /**
28950          * @event beforeselectfile
28951          * Fire before select file
28952          * @param {Roo.bootstrap.DocumentManager} this
28953          */
28954         "beforeselectfile" : true,
28955         /**
28956          * @event process
28957          * Fire before process file
28958          * @param {Roo.bootstrap.DocumentManager} this
28959          * @param {Object} file
28960          */
28961         "process" : true,
28962         /**
28963          * @event previewrendered
28964          * Fire when preview rendered
28965          * @param {Roo.bootstrap.DocumentManager} this
28966          * @param {Object} file
28967          */
28968         "previewrendered" : true,
28969         /**
28970          */
28971         "previewResize" : true
28972         
28973     });
28974 };
28975
28976 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28977     
28978     boxes : 0,
28979     inputName : '',
28980     thumbSize : 300,
28981     multiple : true,
28982     files : false,
28983     method : 'POST',
28984     url : '',
28985     paramName : 'imageUpload',
28986     toolTipName : 'filename',
28987     fieldLabel : '',
28988     labelWidth : 4,
28989     labelAlign : 'left',
28990     editable : true,
28991     delegates : false,
28992     xhr : false, 
28993     
28994     labellg : 0,
28995     labelmd : 0,
28996     labelsm : 0,
28997     labelxs : 0,
28998     
28999     getAutoCreate : function()
29000     {   
29001         var managerWidget = {
29002             tag : 'div',
29003             cls : 'roo-document-manager',
29004             cn : [
29005                 {
29006                     tag : 'input',
29007                     cls : 'roo-document-manager-selector',
29008                     type : 'file'
29009                 },
29010                 {
29011                     tag : 'div',
29012                     cls : 'roo-document-manager-uploader',
29013                     cn : [
29014                         {
29015                             tag : 'div',
29016                             cls : 'roo-document-manager-upload-btn',
29017                             html : '<i class="fa fa-plus"></i>'
29018                         }
29019                     ]
29020                     
29021                 }
29022             ]
29023         };
29024         
29025         var content = [
29026             {
29027                 tag : 'div',
29028                 cls : 'column col-md-12',
29029                 cn : managerWidget
29030             }
29031         ];
29032         
29033         if(this.fieldLabel.length){
29034             
29035             content = [
29036                 {
29037                     tag : 'div',
29038                     cls : 'column col-md-12',
29039                     html : this.fieldLabel
29040                 },
29041                 {
29042                     tag : 'div',
29043                     cls : 'column col-md-12',
29044                     cn : managerWidget
29045                 }
29046             ];
29047
29048             if(this.labelAlign == 'left'){
29049                 content = [
29050                     {
29051                         tag : 'div',
29052                         cls : 'column',
29053                         html : this.fieldLabel
29054                     },
29055                     {
29056                         tag : 'div',
29057                         cls : 'column',
29058                         cn : managerWidget
29059                     }
29060                 ];
29061                 
29062                 if(this.labelWidth > 12){
29063                     content[0].style = "width: " + this.labelWidth + 'px';
29064                 }
29065
29066                 if(this.labelWidth < 13 && this.labelmd == 0){
29067                     this.labelmd = this.labelWidth;
29068                 }
29069
29070                 if(this.labellg > 0){
29071                     content[0].cls += ' col-lg-' + this.labellg;
29072                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29073                 }
29074
29075                 if(this.labelmd > 0){
29076                     content[0].cls += ' col-md-' + this.labelmd;
29077                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29078                 }
29079
29080                 if(this.labelsm > 0){
29081                     content[0].cls += ' col-sm-' + this.labelsm;
29082                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29083                 }
29084
29085                 if(this.labelxs > 0){
29086                     content[0].cls += ' col-xs-' + this.labelxs;
29087                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29088                 }
29089                 
29090             }
29091         }
29092         
29093         var cfg = {
29094             tag : 'div',
29095             cls : 'row clearfix',
29096             cn : content
29097         };
29098         
29099         return cfg;
29100         
29101     },
29102     
29103     initEvents : function()
29104     {
29105         this.managerEl = this.el.select('.roo-document-manager', true).first();
29106         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29107         
29108         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29109         this.selectorEl.hide();
29110         
29111         if(this.multiple){
29112             this.selectorEl.attr('multiple', 'multiple');
29113         }
29114         
29115         this.selectorEl.on('change', this.onFileSelected, this);
29116         
29117         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29118         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29119         
29120         this.uploader.on('click', this.onUploaderClick, this);
29121         
29122         this.renderProgressDialog();
29123         
29124         var _this = this;
29125         
29126         window.addEventListener("resize", function() { _this.refresh(); } );
29127         
29128         this.fireEvent('initial', this);
29129     },
29130     
29131     renderProgressDialog : function()
29132     {
29133         var _this = this;
29134         
29135         this.progressDialog = new Roo.bootstrap.Modal({
29136             cls : 'roo-document-manager-progress-dialog',
29137             allow_close : false,
29138             title : '',
29139             buttons : [
29140                 {
29141                     name  :'cancel',
29142                     weight : 'danger',
29143                     html : 'Cancel'
29144                 }
29145             ], 
29146             listeners : { 
29147                 btnclick : function() {
29148                     _this.uploadCancel();
29149                     this.hide();
29150                 }
29151             }
29152         });
29153          
29154         this.progressDialog.render(Roo.get(document.body));
29155          
29156         this.progress = new Roo.bootstrap.Progress({
29157             cls : 'roo-document-manager-progress',
29158             active : true,
29159             striped : true
29160         });
29161         
29162         this.progress.render(this.progressDialog.getChildContainer());
29163         
29164         this.progressBar = new Roo.bootstrap.ProgressBar({
29165             cls : 'roo-document-manager-progress-bar',
29166             aria_valuenow : 0,
29167             aria_valuemin : 0,
29168             aria_valuemax : 12,
29169             panel : 'success'
29170         });
29171         
29172         this.progressBar.render(this.progress.getChildContainer());
29173     },
29174     
29175     onUploaderClick : function(e)
29176     {
29177         e.preventDefault();
29178      
29179         if(this.fireEvent('beforeselectfile', this) != false){
29180             this.selectorEl.dom.click();
29181         }
29182         
29183     },
29184     
29185     onFileSelected : function(e)
29186     {
29187         e.preventDefault();
29188         
29189         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29190             return;
29191         }
29192         
29193         Roo.each(this.selectorEl.dom.files, function(file){
29194             if(this.fireEvent('inspect', this, file) != false){
29195                 this.files.push(file);
29196             }
29197         }, this);
29198         
29199         this.queue();
29200         
29201     },
29202     
29203     queue : function()
29204     {
29205         this.selectorEl.dom.value = '';
29206         
29207         if(!this.files || !this.files.length){
29208             return;
29209         }
29210         
29211         if(this.boxes > 0 && this.files.length > this.boxes){
29212             this.files = this.files.slice(0, this.boxes);
29213         }
29214         
29215         this.uploader.show();
29216         
29217         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29218             this.uploader.hide();
29219         }
29220         
29221         var _this = this;
29222         
29223         var files = [];
29224         
29225         var docs = [];
29226         
29227         Roo.each(this.files, function(file){
29228             
29229             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29230                 var f = this.renderPreview(file);
29231                 files.push(f);
29232                 return;
29233             }
29234             
29235             if(file.type.indexOf('image') != -1){
29236                 this.delegates.push(
29237                     (function(){
29238                         _this.process(file);
29239                     }).createDelegate(this)
29240                 );
29241         
29242                 return;
29243             }
29244             
29245             docs.push(
29246                 (function(){
29247                     _this.process(file);
29248                 }).createDelegate(this)
29249             );
29250             
29251         }, this);
29252         
29253         this.files = files;
29254         
29255         this.delegates = this.delegates.concat(docs);
29256         
29257         if(!this.delegates.length){
29258             this.refresh();
29259             return;
29260         }
29261         
29262         this.progressBar.aria_valuemax = this.delegates.length;
29263         
29264         this.arrange();
29265         
29266         return;
29267     },
29268     
29269     arrange : function()
29270     {
29271         if(!this.delegates.length){
29272             this.progressDialog.hide();
29273             this.refresh();
29274             return;
29275         }
29276         
29277         var delegate = this.delegates.shift();
29278         
29279         this.progressDialog.show();
29280         
29281         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29282         
29283         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29284         
29285         delegate();
29286     },
29287     
29288     refresh : function()
29289     {
29290         this.uploader.show();
29291         
29292         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29293             this.uploader.hide();
29294         }
29295         
29296         Roo.isTouch ? this.closable(false) : this.closable(true);
29297         
29298         this.fireEvent('refresh', this);
29299     },
29300     
29301     onRemove : function(e, el, o)
29302     {
29303         e.preventDefault();
29304         
29305         this.fireEvent('remove', this, o);
29306         
29307     },
29308     
29309     remove : function(o)
29310     {
29311         var files = [];
29312         
29313         Roo.each(this.files, function(file){
29314             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29315                 files.push(file);
29316                 return;
29317             }
29318
29319             o.target.remove();
29320
29321         }, this);
29322         
29323         this.files = files;
29324         
29325         this.refresh();
29326     },
29327     
29328     clear : function()
29329     {
29330         Roo.each(this.files, function(file){
29331             if(!file.target){
29332                 return;
29333             }
29334             
29335             file.target.remove();
29336
29337         }, this);
29338         
29339         this.files = [];
29340         
29341         this.refresh();
29342     },
29343     
29344     onClick : function(e, el, o)
29345     {
29346         e.preventDefault();
29347         
29348         this.fireEvent('click', this, o);
29349         
29350     },
29351     
29352     closable : function(closable)
29353     {
29354         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29355             
29356             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29357             
29358             if(closable){
29359                 el.show();
29360                 return;
29361             }
29362             
29363             el.hide();
29364             
29365         }, this);
29366     },
29367     
29368     xhrOnLoad : function(xhr)
29369     {
29370         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29371             el.remove();
29372         }, this);
29373         
29374         if (xhr.readyState !== 4) {
29375             this.arrange();
29376             this.fireEvent('exception', this, xhr);
29377             return;
29378         }
29379
29380         var response = Roo.decode(xhr.responseText);
29381         
29382         if(!response.success){
29383             this.arrange();
29384             this.fireEvent('exception', this, xhr);
29385             return;
29386         }
29387         
29388         var file = this.renderPreview(response.data);
29389         
29390         this.files.push(file);
29391         
29392         this.arrange();
29393         
29394         this.fireEvent('afterupload', this, xhr);
29395         
29396     },
29397     
29398     xhrOnError : function(xhr)
29399     {
29400         Roo.log('xhr on error');
29401         
29402         var response = Roo.decode(xhr.responseText);
29403           
29404         Roo.log(response);
29405         
29406         this.arrange();
29407     },
29408     
29409     process : function(file)
29410     {
29411         if(this.fireEvent('process', this, file) !== false){
29412             if(this.editable && file.type.indexOf('image') != -1){
29413                 this.fireEvent('edit', this, file);
29414                 return;
29415             }
29416
29417             this.uploadStart(file, false);
29418
29419             return;
29420         }
29421         
29422     },
29423     
29424     uploadStart : function(file, crop)
29425     {
29426         this.xhr = new XMLHttpRequest();
29427         
29428         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29429             this.arrange();
29430             return;
29431         }
29432         
29433         file.xhr = this.xhr;
29434             
29435         this.managerEl.createChild({
29436             tag : 'div',
29437             cls : 'roo-document-manager-loading',
29438             cn : [
29439                 {
29440                     tag : 'div',
29441                     tooltip : file.name,
29442                     cls : 'roo-document-manager-thumb',
29443                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29444                 }
29445             ]
29446
29447         });
29448
29449         this.xhr.open(this.method, this.url, true);
29450         
29451         var headers = {
29452             "Accept": "application/json",
29453             "Cache-Control": "no-cache",
29454             "X-Requested-With": "XMLHttpRequest"
29455         };
29456         
29457         for (var headerName in headers) {
29458             var headerValue = headers[headerName];
29459             if (headerValue) {
29460                 this.xhr.setRequestHeader(headerName, headerValue);
29461             }
29462         }
29463         
29464         var _this = this;
29465         
29466         this.xhr.onload = function()
29467         {
29468             _this.xhrOnLoad(_this.xhr);
29469         }
29470         
29471         this.xhr.onerror = function()
29472         {
29473             _this.xhrOnError(_this.xhr);
29474         }
29475         
29476         var formData = new FormData();
29477
29478         formData.append('returnHTML', 'NO');
29479         
29480         if(crop){
29481             formData.append('crop', crop);
29482         }
29483         
29484         formData.append(this.paramName, file, file.name);
29485         
29486         var options = {
29487             file : file, 
29488             manually : false
29489         };
29490         
29491         if(this.fireEvent('prepare', this, formData, options) != false){
29492             
29493             if(options.manually){
29494                 return;
29495             }
29496             
29497             this.xhr.send(formData);
29498             return;
29499         };
29500         
29501         this.uploadCancel();
29502     },
29503     
29504     uploadCancel : function()
29505     {
29506         if (this.xhr) {
29507             this.xhr.abort();
29508         }
29509         
29510         this.delegates = [];
29511         
29512         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29513             el.remove();
29514         }, this);
29515         
29516         this.arrange();
29517     },
29518     
29519     renderPreview : function(file)
29520     {
29521         if(typeof(file.target) != 'undefined' && file.target){
29522             return file;
29523         }
29524         
29525         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29526         
29527         var previewEl = this.managerEl.createChild({
29528             tag : 'div',
29529             cls : 'roo-document-manager-preview',
29530             cn : [
29531                 {
29532                     tag : 'div',
29533                     tooltip : file[this.toolTipName],
29534                     cls : 'roo-document-manager-thumb',
29535                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29536                 },
29537                 {
29538                     tag : 'button',
29539                     cls : 'close',
29540                     html : '<i class="fa fa-times-circle"></i>'
29541                 }
29542             ]
29543         });
29544
29545         var close = previewEl.select('button.close', true).first();
29546
29547         close.on('click', this.onRemove, this, file);
29548
29549         file.target = previewEl;
29550
29551         var image = previewEl.select('img', true).first();
29552         
29553         var _this = this;
29554         
29555         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29556         
29557         image.on('click', this.onClick, this, file);
29558         
29559         this.fireEvent('previewrendered', this, file);
29560         
29561         return file;
29562         
29563     },
29564     
29565     onPreviewLoad : function(file, image)
29566     {
29567         if(typeof(file.target) == 'undefined' || !file.target){
29568             return;
29569         }
29570         
29571         var width = image.dom.naturalWidth || image.dom.width;
29572         var height = image.dom.naturalHeight || image.dom.height;
29573         
29574         if(!this.previewResize) {
29575             return;
29576         }
29577         
29578         if(width > height){
29579             file.target.addClass('wide');
29580             return;
29581         }
29582         
29583         file.target.addClass('tall');
29584         return;
29585         
29586     },
29587     
29588     uploadFromSource : function(file, crop)
29589     {
29590         this.xhr = new XMLHttpRequest();
29591         
29592         this.managerEl.createChild({
29593             tag : 'div',
29594             cls : 'roo-document-manager-loading',
29595             cn : [
29596                 {
29597                     tag : 'div',
29598                     tooltip : file.name,
29599                     cls : 'roo-document-manager-thumb',
29600                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29601                 }
29602             ]
29603
29604         });
29605
29606         this.xhr.open(this.method, this.url, true);
29607         
29608         var headers = {
29609             "Accept": "application/json",
29610             "Cache-Control": "no-cache",
29611             "X-Requested-With": "XMLHttpRequest"
29612         };
29613         
29614         for (var headerName in headers) {
29615             var headerValue = headers[headerName];
29616             if (headerValue) {
29617                 this.xhr.setRequestHeader(headerName, headerValue);
29618             }
29619         }
29620         
29621         var _this = this;
29622         
29623         this.xhr.onload = function()
29624         {
29625             _this.xhrOnLoad(_this.xhr);
29626         }
29627         
29628         this.xhr.onerror = function()
29629         {
29630             _this.xhrOnError(_this.xhr);
29631         }
29632         
29633         var formData = new FormData();
29634
29635         formData.append('returnHTML', 'NO');
29636         
29637         formData.append('crop', crop);
29638         
29639         if(typeof(file.filename) != 'undefined'){
29640             formData.append('filename', file.filename);
29641         }
29642         
29643         if(typeof(file.mimetype) != 'undefined'){
29644             formData.append('mimetype', file.mimetype);
29645         }
29646         
29647         Roo.log(formData);
29648         
29649         if(this.fireEvent('prepare', this, formData) != false){
29650             this.xhr.send(formData);
29651         };
29652     }
29653 });
29654
29655 /*
29656 * Licence: LGPL
29657 */
29658
29659 /**
29660  * @class Roo.bootstrap.DocumentViewer
29661  * @extends Roo.bootstrap.Component
29662  * Bootstrap DocumentViewer class
29663  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29664  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29665  * 
29666  * @constructor
29667  * Create a new DocumentViewer
29668  * @param {Object} config The config object
29669  */
29670
29671 Roo.bootstrap.DocumentViewer = function(config){
29672     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29673     
29674     this.addEvents({
29675         /**
29676          * @event initial
29677          * Fire after initEvent
29678          * @param {Roo.bootstrap.DocumentViewer} this
29679          */
29680         "initial" : true,
29681         /**
29682          * @event click
29683          * Fire after click
29684          * @param {Roo.bootstrap.DocumentViewer} this
29685          */
29686         "click" : true,
29687         /**
29688          * @event download
29689          * Fire after download button
29690          * @param {Roo.bootstrap.DocumentViewer} this
29691          */
29692         "download" : true,
29693         /**
29694          * @event trash
29695          * Fire after trash button
29696          * @param {Roo.bootstrap.DocumentViewer} this
29697          */
29698         "trash" : true
29699         
29700     });
29701 };
29702
29703 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29704     
29705     showDownload : true,
29706     
29707     showTrash : true,
29708     
29709     getAutoCreate : function()
29710     {
29711         var cfg = {
29712             tag : 'div',
29713             cls : 'roo-document-viewer',
29714             cn : [
29715                 {
29716                     tag : 'div',
29717                     cls : 'roo-document-viewer-body',
29718                     cn : [
29719                         {
29720                             tag : 'div',
29721                             cls : 'roo-document-viewer-thumb',
29722                             cn : [
29723                                 {
29724                                     tag : 'img',
29725                                     cls : 'roo-document-viewer-image'
29726                                 }
29727                             ]
29728                         }
29729                     ]
29730                 },
29731                 {
29732                     tag : 'div',
29733                     cls : 'roo-document-viewer-footer',
29734                     cn : {
29735                         tag : 'div',
29736                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29737                         cn : [
29738                             {
29739                                 tag : 'div',
29740                                 cls : 'btn-group roo-document-viewer-download',
29741                                 cn : [
29742                                     {
29743                                         tag : 'button',
29744                                         cls : 'btn btn-default',
29745                                         html : '<i class="fa fa-download"></i>'
29746                                     }
29747                                 ]
29748                             },
29749                             {
29750                                 tag : 'div',
29751                                 cls : 'btn-group roo-document-viewer-trash',
29752                                 cn : [
29753                                     {
29754                                         tag : 'button',
29755                                         cls : 'btn btn-default',
29756                                         html : '<i class="fa fa-trash"></i>'
29757                                     }
29758                                 ]
29759                             }
29760                         ]
29761                     }
29762                 }
29763             ]
29764         };
29765         
29766         return cfg;
29767     },
29768     
29769     initEvents : function()
29770     {
29771         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29772         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29773         
29774         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29775         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29776         
29777         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29778         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29779         
29780         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29781         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29782         
29783         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29784         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29785         
29786         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29787         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29788         
29789         this.bodyEl.on('click', this.onClick, this);
29790         this.downloadBtn.on('click', this.onDownload, this);
29791         this.trashBtn.on('click', this.onTrash, this);
29792         
29793         this.downloadBtn.hide();
29794         this.trashBtn.hide();
29795         
29796         if(this.showDownload){
29797             this.downloadBtn.show();
29798         }
29799         
29800         if(this.showTrash){
29801             this.trashBtn.show();
29802         }
29803         
29804         if(!this.showDownload && !this.showTrash) {
29805             this.footerEl.hide();
29806         }
29807         
29808     },
29809     
29810     initial : function()
29811     {
29812         this.fireEvent('initial', this);
29813         
29814     },
29815     
29816     onClick : function(e)
29817     {
29818         e.preventDefault();
29819         
29820         this.fireEvent('click', this);
29821     },
29822     
29823     onDownload : function(e)
29824     {
29825         e.preventDefault();
29826         
29827         this.fireEvent('download', this);
29828     },
29829     
29830     onTrash : function(e)
29831     {
29832         e.preventDefault();
29833         
29834         this.fireEvent('trash', this);
29835     }
29836     
29837 });
29838 /*
29839  * - LGPL
29840  *
29841  * nav progress bar
29842  * 
29843  */
29844
29845 /**
29846  * @class Roo.bootstrap.NavProgressBar
29847  * @extends Roo.bootstrap.Component
29848  * Bootstrap NavProgressBar class
29849  * 
29850  * @constructor
29851  * Create a new nav progress bar
29852  * @param {Object} config The config object
29853  */
29854
29855 Roo.bootstrap.NavProgressBar = function(config){
29856     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29857
29858     this.bullets = this.bullets || [];
29859    
29860 //    Roo.bootstrap.NavProgressBar.register(this);
29861      this.addEvents({
29862         /**
29863              * @event changed
29864              * Fires when the active item changes
29865              * @param {Roo.bootstrap.NavProgressBar} this
29866              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29867              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29868          */
29869         'changed': true
29870      });
29871     
29872 };
29873
29874 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29875     
29876     bullets : [],
29877     barItems : [],
29878     
29879     getAutoCreate : function()
29880     {
29881         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29882         
29883         cfg = {
29884             tag : 'div',
29885             cls : 'roo-navigation-bar-group',
29886             cn : [
29887                 {
29888                     tag : 'div',
29889                     cls : 'roo-navigation-top-bar'
29890                 },
29891                 {
29892                     tag : 'div',
29893                     cls : 'roo-navigation-bullets-bar',
29894                     cn : [
29895                         {
29896                             tag : 'ul',
29897                             cls : 'roo-navigation-bar'
29898                         }
29899                     ]
29900                 },
29901                 
29902                 {
29903                     tag : 'div',
29904                     cls : 'roo-navigation-bottom-bar'
29905                 }
29906             ]
29907             
29908         };
29909         
29910         return cfg;
29911         
29912     },
29913     
29914     initEvents: function() 
29915     {
29916         
29917     },
29918     
29919     onRender : function(ct, position) 
29920     {
29921         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29922         
29923         if(this.bullets.length){
29924             Roo.each(this.bullets, function(b){
29925                this.addItem(b);
29926             }, this);
29927         }
29928         
29929         this.format();
29930         
29931     },
29932     
29933     addItem : function(cfg)
29934     {
29935         var item = new Roo.bootstrap.NavProgressItem(cfg);
29936         
29937         item.parentId = this.id;
29938         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29939         
29940         if(cfg.html){
29941             var top = new Roo.bootstrap.Element({
29942                 tag : 'div',
29943                 cls : 'roo-navigation-bar-text'
29944             });
29945             
29946             var bottom = new Roo.bootstrap.Element({
29947                 tag : 'div',
29948                 cls : 'roo-navigation-bar-text'
29949             });
29950             
29951             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29952             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29953             
29954             var topText = new Roo.bootstrap.Element({
29955                 tag : 'span',
29956                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29957             });
29958             
29959             var bottomText = new Roo.bootstrap.Element({
29960                 tag : 'span',
29961                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29962             });
29963             
29964             topText.onRender(top.el, null);
29965             bottomText.onRender(bottom.el, null);
29966             
29967             item.topEl = top;
29968             item.bottomEl = bottom;
29969         }
29970         
29971         this.barItems.push(item);
29972         
29973         return item;
29974     },
29975     
29976     getActive : function()
29977     {
29978         var active = false;
29979         
29980         Roo.each(this.barItems, function(v){
29981             
29982             if (!v.isActive()) {
29983                 return;
29984             }
29985             
29986             active = v;
29987             return false;
29988             
29989         });
29990         
29991         return active;
29992     },
29993     
29994     setActiveItem : function(item)
29995     {
29996         var prev = false;
29997         
29998         Roo.each(this.barItems, function(v){
29999             if (v.rid == item.rid) {
30000                 return ;
30001             }
30002             
30003             if (v.isActive()) {
30004                 v.setActive(false);
30005                 prev = v;
30006             }
30007         });
30008
30009         item.setActive(true);
30010         
30011         this.fireEvent('changed', this, item, prev);
30012     },
30013     
30014     getBarItem: function(rid)
30015     {
30016         var ret = false;
30017         
30018         Roo.each(this.barItems, function(e) {
30019             if (e.rid != rid) {
30020                 return;
30021             }
30022             
30023             ret =  e;
30024             return false;
30025         });
30026         
30027         return ret;
30028     },
30029     
30030     indexOfItem : function(item)
30031     {
30032         var index = false;
30033         
30034         Roo.each(this.barItems, function(v, i){
30035             
30036             if (v.rid != item.rid) {
30037                 return;
30038             }
30039             
30040             index = i;
30041             return false
30042         });
30043         
30044         return index;
30045     },
30046     
30047     setActiveNext : function()
30048     {
30049         var i = this.indexOfItem(this.getActive());
30050         
30051         if (i > this.barItems.length) {
30052             return;
30053         }
30054         
30055         this.setActiveItem(this.barItems[i+1]);
30056     },
30057     
30058     setActivePrev : function()
30059     {
30060         var i = this.indexOfItem(this.getActive());
30061         
30062         if (i  < 1) {
30063             return;
30064         }
30065         
30066         this.setActiveItem(this.barItems[i-1]);
30067     },
30068     
30069     format : function()
30070     {
30071         if(!this.barItems.length){
30072             return;
30073         }
30074      
30075         var width = 100 / this.barItems.length;
30076         
30077         Roo.each(this.barItems, function(i){
30078             i.el.setStyle('width', width + '%');
30079             i.topEl.el.setStyle('width', width + '%');
30080             i.bottomEl.el.setStyle('width', width + '%');
30081         }, this);
30082         
30083     }
30084     
30085 });
30086 /*
30087  * - LGPL
30088  *
30089  * Nav Progress Item
30090  * 
30091  */
30092
30093 /**
30094  * @class Roo.bootstrap.NavProgressItem
30095  * @extends Roo.bootstrap.Component
30096  * Bootstrap NavProgressItem class
30097  * @cfg {String} rid the reference id
30098  * @cfg {Boolean} active (true|false) Is item active default false
30099  * @cfg {Boolean} disabled (true|false) Is item active default false
30100  * @cfg {String} html
30101  * @cfg {String} position (top|bottom) text position default bottom
30102  * @cfg {String} icon show icon instead of number
30103  * 
30104  * @constructor
30105  * Create a new NavProgressItem
30106  * @param {Object} config The config object
30107  */
30108 Roo.bootstrap.NavProgressItem = function(config){
30109     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30110     this.addEvents({
30111         // raw events
30112         /**
30113          * @event click
30114          * The raw click event for the entire grid.
30115          * @param {Roo.bootstrap.NavProgressItem} this
30116          * @param {Roo.EventObject} e
30117          */
30118         "click" : true
30119     });
30120    
30121 };
30122
30123 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30124     
30125     rid : '',
30126     active : false,
30127     disabled : false,
30128     html : '',
30129     position : 'bottom',
30130     icon : false,
30131     
30132     getAutoCreate : function()
30133     {
30134         var iconCls = 'roo-navigation-bar-item-icon';
30135         
30136         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30137         
30138         var cfg = {
30139             tag: 'li',
30140             cls: 'roo-navigation-bar-item',
30141             cn : [
30142                 {
30143                     tag : 'i',
30144                     cls : iconCls
30145                 }
30146             ]
30147         };
30148         
30149         if(this.active){
30150             cfg.cls += ' active';
30151         }
30152         if(this.disabled){
30153             cfg.cls += ' disabled';
30154         }
30155         
30156         return cfg;
30157     },
30158     
30159     disable : function()
30160     {
30161         this.setDisabled(true);
30162     },
30163     
30164     enable : function()
30165     {
30166         this.setDisabled(false);
30167     },
30168     
30169     initEvents: function() 
30170     {
30171         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30172         
30173         this.iconEl.on('click', this.onClick, this);
30174     },
30175     
30176     onClick : function(e)
30177     {
30178         e.preventDefault();
30179         
30180         if(this.disabled){
30181             return;
30182         }
30183         
30184         if(this.fireEvent('click', this, e) === false){
30185             return;
30186         };
30187         
30188         this.parent().setActiveItem(this);
30189     },
30190     
30191     isActive: function () 
30192     {
30193         return this.active;
30194     },
30195     
30196     setActive : function(state)
30197     {
30198         if(this.active == state){
30199             return;
30200         }
30201         
30202         this.active = state;
30203         
30204         if (state) {
30205             this.el.addClass('active');
30206             return;
30207         }
30208         
30209         this.el.removeClass('active');
30210         
30211         return;
30212     },
30213     
30214     setDisabled : function(state)
30215     {
30216         if(this.disabled == state){
30217             return;
30218         }
30219         
30220         this.disabled = state;
30221         
30222         if (state) {
30223             this.el.addClass('disabled');
30224             return;
30225         }
30226         
30227         this.el.removeClass('disabled');
30228     },
30229     
30230     tooltipEl : function()
30231     {
30232         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30233     }
30234 });
30235  
30236
30237  /*
30238  * - LGPL
30239  *
30240  * FieldLabel
30241  * 
30242  */
30243
30244 /**
30245  * @class Roo.bootstrap.FieldLabel
30246  * @extends Roo.bootstrap.Component
30247  * Bootstrap FieldLabel class
30248  * @cfg {String} html contents of the element
30249  * @cfg {String} tag tag of the element default label
30250  * @cfg {String} cls class of the element
30251  * @cfg {String} target label target 
30252  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30253  * @cfg {String} invalidClass default "text-warning"
30254  * @cfg {String} validClass default "text-success"
30255  * @cfg {String} iconTooltip default "This field is required"
30256  * @cfg {String} indicatorpos (left|right) default left
30257  * 
30258  * @constructor
30259  * Create a new FieldLabel
30260  * @param {Object} config The config object
30261  */
30262
30263 Roo.bootstrap.FieldLabel = function(config){
30264     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30265     
30266     this.addEvents({
30267             /**
30268              * @event invalid
30269              * Fires after the field has been marked as invalid.
30270              * @param {Roo.form.FieldLabel} this
30271              * @param {String} msg The validation message
30272              */
30273             invalid : true,
30274             /**
30275              * @event valid
30276              * Fires after the field has been validated with no errors.
30277              * @param {Roo.form.FieldLabel} this
30278              */
30279             valid : true
30280         });
30281 };
30282
30283 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30284     
30285     tag: 'label',
30286     cls: '',
30287     html: '',
30288     target: '',
30289     allowBlank : true,
30290     invalidClass : 'has-warning',
30291     validClass : 'has-success',
30292     iconTooltip : 'This field is required',
30293     indicatorpos : 'left',
30294     
30295     getAutoCreate : function(){
30296         
30297         var cls = "";
30298         if (!this.allowBlank) {
30299             cls  = "visible";
30300         }
30301         
30302         var cfg = {
30303             tag : this.tag,
30304             cls : 'roo-bootstrap-field-label ' + this.cls,
30305             for : this.target,
30306             cn : [
30307                 {
30308                     tag : 'i',
30309                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30310                     tooltip : this.iconTooltip
30311                 },
30312                 {
30313                     tag : 'span',
30314                     html : this.html
30315                 }
30316             ] 
30317         };
30318         
30319         if(this.indicatorpos == 'right'){
30320             var cfg = {
30321                 tag : this.tag,
30322                 cls : 'roo-bootstrap-field-label ' + this.cls,
30323                 for : this.target,
30324                 cn : [
30325                     {
30326                         tag : 'span',
30327                         html : this.html
30328                     },
30329                     {
30330                         tag : 'i',
30331                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30332                         tooltip : this.iconTooltip
30333                     }
30334                 ] 
30335             };
30336         }
30337         
30338         return cfg;
30339     },
30340     
30341     initEvents: function() 
30342     {
30343         Roo.bootstrap.Element.superclass.initEvents.call(this);
30344         
30345         this.indicator = this.indicatorEl();
30346         
30347         if(this.indicator){
30348             this.indicator.removeClass('visible');
30349             this.indicator.addClass('invisible');
30350         }
30351         
30352         Roo.bootstrap.FieldLabel.register(this);
30353     },
30354     
30355     indicatorEl : function()
30356     {
30357         var indicator = this.el.select('i.roo-required-indicator',true).first();
30358         
30359         if(!indicator){
30360             return false;
30361         }
30362         
30363         return indicator;
30364         
30365     },
30366     
30367     /**
30368      * Mark this field as valid
30369      */
30370     markValid : function()
30371     {
30372         if(this.indicator){
30373             this.indicator.removeClass('visible');
30374             this.indicator.addClass('invisible');
30375         }
30376         
30377         this.el.removeClass(this.invalidClass);
30378         
30379         this.el.addClass(this.validClass);
30380         
30381         this.fireEvent('valid', this);
30382     },
30383     
30384     /**
30385      * Mark this field as invalid
30386      * @param {String} msg The validation message
30387      */
30388     markInvalid : function(msg)
30389     {
30390         if(this.indicator){
30391             this.indicator.removeClass('invisible');
30392             this.indicator.addClass('visible');
30393         }
30394         
30395         this.el.removeClass(this.validClass);
30396         
30397         this.el.addClass(this.invalidClass);
30398         
30399         this.fireEvent('invalid', this, msg);
30400     }
30401     
30402    
30403 });
30404
30405 Roo.apply(Roo.bootstrap.FieldLabel, {
30406     
30407     groups: {},
30408     
30409      /**
30410     * register a FieldLabel Group
30411     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30412     */
30413     register : function(label)
30414     {
30415         if(this.groups.hasOwnProperty(label.target)){
30416             return;
30417         }
30418      
30419         this.groups[label.target] = label;
30420         
30421     },
30422     /**
30423     * fetch a FieldLabel Group based on the target
30424     * @param {string} target
30425     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30426     */
30427     get: function(target) {
30428         if (typeof(this.groups[target]) == 'undefined') {
30429             return false;
30430         }
30431         
30432         return this.groups[target] ;
30433     }
30434 });
30435
30436  
30437
30438  /*
30439  * - LGPL
30440  *
30441  * page DateSplitField.
30442  * 
30443  */
30444
30445
30446 /**
30447  * @class Roo.bootstrap.DateSplitField
30448  * @extends Roo.bootstrap.Component
30449  * Bootstrap DateSplitField class
30450  * @cfg {string} fieldLabel - the label associated
30451  * @cfg {Number} labelWidth set the width of label (0-12)
30452  * @cfg {String} labelAlign (top|left)
30453  * @cfg {Boolean} dayAllowBlank (true|false) default false
30454  * @cfg {Boolean} monthAllowBlank (true|false) default false
30455  * @cfg {Boolean} yearAllowBlank (true|false) default false
30456  * @cfg {string} dayPlaceholder 
30457  * @cfg {string} monthPlaceholder
30458  * @cfg {string} yearPlaceholder
30459  * @cfg {string} dayFormat default 'd'
30460  * @cfg {string} monthFormat default 'm'
30461  * @cfg {string} yearFormat default 'Y'
30462  * @cfg {Number} labellg set the width of label (1-12)
30463  * @cfg {Number} labelmd set the width of label (1-12)
30464  * @cfg {Number} labelsm set the width of label (1-12)
30465  * @cfg {Number} labelxs set the width of label (1-12)
30466
30467  *     
30468  * @constructor
30469  * Create a new DateSplitField
30470  * @param {Object} config The config object
30471  */
30472
30473 Roo.bootstrap.DateSplitField = function(config){
30474     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30475     
30476     this.addEvents({
30477         // raw events
30478          /**
30479          * @event years
30480          * getting the data of years
30481          * @param {Roo.bootstrap.DateSplitField} this
30482          * @param {Object} years
30483          */
30484         "years" : true,
30485         /**
30486          * @event days
30487          * getting the data of days
30488          * @param {Roo.bootstrap.DateSplitField} this
30489          * @param {Object} days
30490          */
30491         "days" : true,
30492         /**
30493          * @event invalid
30494          * Fires after the field has been marked as invalid.
30495          * @param {Roo.form.Field} this
30496          * @param {String} msg The validation message
30497          */
30498         invalid : true,
30499        /**
30500          * @event valid
30501          * Fires after the field has been validated with no errors.
30502          * @param {Roo.form.Field} this
30503          */
30504         valid : true
30505     });
30506 };
30507
30508 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30509     
30510     fieldLabel : '',
30511     labelAlign : 'top',
30512     labelWidth : 3,
30513     dayAllowBlank : false,
30514     monthAllowBlank : false,
30515     yearAllowBlank : false,
30516     dayPlaceholder : '',
30517     monthPlaceholder : '',
30518     yearPlaceholder : '',
30519     dayFormat : 'd',
30520     monthFormat : 'm',
30521     yearFormat : 'Y',
30522     isFormField : true,
30523     labellg : 0,
30524     labelmd : 0,
30525     labelsm : 0,
30526     labelxs : 0,
30527     
30528     getAutoCreate : function()
30529     {
30530         var cfg = {
30531             tag : 'div',
30532             cls : 'row roo-date-split-field-group',
30533             cn : [
30534                 {
30535                     tag : 'input',
30536                     type : 'hidden',
30537                     cls : 'form-hidden-field roo-date-split-field-group-value',
30538                     name : this.name
30539                 }
30540             ]
30541         };
30542         
30543         var labelCls = 'col-md-12';
30544         var contentCls = 'col-md-4';
30545         
30546         if(this.fieldLabel){
30547             
30548             var label = {
30549                 tag : 'div',
30550                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30551                 cn : [
30552                     {
30553                         tag : 'label',
30554                         html : this.fieldLabel
30555                     }
30556                 ]
30557             };
30558             
30559             if(this.labelAlign == 'left'){
30560             
30561                 if(this.labelWidth > 12){
30562                     label.style = "width: " + this.labelWidth + 'px';
30563                 }
30564
30565                 if(this.labelWidth < 13 && this.labelmd == 0){
30566                     this.labelmd = this.labelWidth;
30567                 }
30568
30569                 if(this.labellg > 0){
30570                     labelCls = ' col-lg-' + this.labellg;
30571                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30572                 }
30573
30574                 if(this.labelmd > 0){
30575                     labelCls = ' col-md-' + this.labelmd;
30576                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30577                 }
30578
30579                 if(this.labelsm > 0){
30580                     labelCls = ' col-sm-' + this.labelsm;
30581                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30582                 }
30583
30584                 if(this.labelxs > 0){
30585                     labelCls = ' col-xs-' + this.labelxs;
30586                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30587                 }
30588             }
30589             
30590             label.cls += ' ' + labelCls;
30591             
30592             cfg.cn.push(label);
30593         }
30594         
30595         Roo.each(['day', 'month', 'year'], function(t){
30596             cfg.cn.push({
30597                 tag : 'div',
30598                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30599             });
30600         }, this);
30601         
30602         return cfg;
30603     },
30604     
30605     inputEl: function ()
30606     {
30607         return this.el.select('.roo-date-split-field-group-value', true).first();
30608     },
30609     
30610     onRender : function(ct, position) 
30611     {
30612         var _this = this;
30613         
30614         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30615         
30616         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30617         
30618         this.dayField = new Roo.bootstrap.ComboBox({
30619             allowBlank : this.dayAllowBlank,
30620             alwaysQuery : true,
30621             displayField : 'value',
30622             editable : false,
30623             fieldLabel : '',
30624             forceSelection : true,
30625             mode : 'local',
30626             placeholder : this.dayPlaceholder,
30627             selectOnFocus : true,
30628             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30629             triggerAction : 'all',
30630             typeAhead : true,
30631             valueField : 'value',
30632             store : new Roo.data.SimpleStore({
30633                 data : (function() {    
30634                     var days = [];
30635                     _this.fireEvent('days', _this, days);
30636                     return days;
30637                 })(),
30638                 fields : [ 'value' ]
30639             }),
30640             listeners : {
30641                 select : function (_self, record, index)
30642                 {
30643                     _this.setValue(_this.getValue());
30644                 }
30645             }
30646         });
30647
30648         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30649         
30650         this.monthField = new Roo.bootstrap.MonthField({
30651             after : '<i class=\"fa fa-calendar\"></i>',
30652             allowBlank : this.monthAllowBlank,
30653             placeholder : this.monthPlaceholder,
30654             readOnly : true,
30655             listeners : {
30656                 render : function (_self)
30657                 {
30658                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30659                         e.preventDefault();
30660                         _self.focus();
30661                     });
30662                 },
30663                 select : function (_self, oldvalue, newvalue)
30664                 {
30665                     _this.setValue(_this.getValue());
30666                 }
30667             }
30668         });
30669         
30670         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30671         
30672         this.yearField = new Roo.bootstrap.ComboBox({
30673             allowBlank : this.yearAllowBlank,
30674             alwaysQuery : true,
30675             displayField : 'value',
30676             editable : false,
30677             fieldLabel : '',
30678             forceSelection : true,
30679             mode : 'local',
30680             placeholder : this.yearPlaceholder,
30681             selectOnFocus : true,
30682             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30683             triggerAction : 'all',
30684             typeAhead : true,
30685             valueField : 'value',
30686             store : new Roo.data.SimpleStore({
30687                 data : (function() {
30688                     var years = [];
30689                     _this.fireEvent('years', _this, years);
30690                     return years;
30691                 })(),
30692                 fields : [ 'value' ]
30693             }),
30694             listeners : {
30695                 select : function (_self, record, index)
30696                 {
30697                     _this.setValue(_this.getValue());
30698                 }
30699             }
30700         });
30701
30702         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30703     },
30704     
30705     setValue : function(v, format)
30706     {
30707         this.inputEl.dom.value = v;
30708         
30709         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30710         
30711         var d = Date.parseDate(v, f);
30712         
30713         if(!d){
30714             this.validate();
30715             return;
30716         }
30717         
30718         this.setDay(d.format(this.dayFormat));
30719         this.setMonth(d.format(this.monthFormat));
30720         this.setYear(d.format(this.yearFormat));
30721         
30722         this.validate();
30723         
30724         return;
30725     },
30726     
30727     setDay : function(v)
30728     {
30729         this.dayField.setValue(v);
30730         this.inputEl.dom.value = this.getValue();
30731         this.validate();
30732         return;
30733     },
30734     
30735     setMonth : function(v)
30736     {
30737         this.monthField.setValue(v, true);
30738         this.inputEl.dom.value = this.getValue();
30739         this.validate();
30740         return;
30741     },
30742     
30743     setYear : function(v)
30744     {
30745         this.yearField.setValue(v);
30746         this.inputEl.dom.value = this.getValue();
30747         this.validate();
30748         return;
30749     },
30750     
30751     getDay : function()
30752     {
30753         return this.dayField.getValue();
30754     },
30755     
30756     getMonth : function()
30757     {
30758         return this.monthField.getValue();
30759     },
30760     
30761     getYear : function()
30762     {
30763         return this.yearField.getValue();
30764     },
30765     
30766     getValue : function()
30767     {
30768         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30769         
30770         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30771         
30772         return date;
30773     },
30774     
30775     reset : function()
30776     {
30777         this.setDay('');
30778         this.setMonth('');
30779         this.setYear('');
30780         this.inputEl.dom.value = '';
30781         this.validate();
30782         return;
30783     },
30784     
30785     validate : function()
30786     {
30787         var d = this.dayField.validate();
30788         var m = this.monthField.validate();
30789         var y = this.yearField.validate();
30790         
30791         var valid = true;
30792         
30793         if(
30794                 (!this.dayAllowBlank && !d) ||
30795                 (!this.monthAllowBlank && !m) ||
30796                 (!this.yearAllowBlank && !y)
30797         ){
30798             valid = false;
30799         }
30800         
30801         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30802             return valid;
30803         }
30804         
30805         if(valid){
30806             this.markValid();
30807             return valid;
30808         }
30809         
30810         this.markInvalid();
30811         
30812         return valid;
30813     },
30814     
30815     markValid : function()
30816     {
30817         
30818         var label = this.el.select('label', true).first();
30819         var icon = this.el.select('i.fa-star', true).first();
30820
30821         if(label && icon){
30822             icon.remove();
30823         }
30824         
30825         this.fireEvent('valid', this);
30826     },
30827     
30828      /**
30829      * Mark this field as invalid
30830      * @param {String} msg The validation message
30831      */
30832     markInvalid : function(msg)
30833     {
30834         
30835         var label = this.el.select('label', true).first();
30836         var icon = this.el.select('i.fa-star', true).first();
30837
30838         if(label && !icon){
30839             this.el.select('.roo-date-split-field-label', true).createChild({
30840                 tag : 'i',
30841                 cls : 'text-danger fa fa-lg fa-star',
30842                 tooltip : 'This field is required',
30843                 style : 'margin-right:5px;'
30844             }, label, true);
30845         }
30846         
30847         this.fireEvent('invalid', this, msg);
30848     },
30849     
30850     clearInvalid : function()
30851     {
30852         var label = this.el.select('label', true).first();
30853         var icon = this.el.select('i.fa-star', true).first();
30854
30855         if(label && icon){
30856             icon.remove();
30857         }
30858         
30859         this.fireEvent('valid', this);
30860     },
30861     
30862     getName: function()
30863     {
30864         return this.name;
30865     }
30866     
30867 });
30868
30869  /**
30870  *
30871  * This is based on 
30872  * http://masonry.desandro.com
30873  *
30874  * The idea is to render all the bricks based on vertical width...
30875  *
30876  * The original code extends 'outlayer' - we might need to use that....
30877  * 
30878  */
30879
30880
30881 /**
30882  * @class Roo.bootstrap.LayoutMasonry
30883  * @extends Roo.bootstrap.Component
30884  * Bootstrap Layout Masonry class
30885  * 
30886  * @constructor
30887  * Create a new Element
30888  * @param {Object} config The config object
30889  */
30890
30891 Roo.bootstrap.LayoutMasonry = function(config){
30892     
30893     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30894     
30895     this.bricks = [];
30896     
30897     Roo.bootstrap.LayoutMasonry.register(this);
30898     
30899     this.addEvents({
30900         // raw events
30901         /**
30902          * @event layout
30903          * Fire after layout the items
30904          * @param {Roo.bootstrap.LayoutMasonry} this
30905          * @param {Roo.EventObject} e
30906          */
30907         "layout" : true
30908     });
30909     
30910 };
30911
30912 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30913     
30914     /**
30915      * @cfg {Boolean} isLayoutInstant = no animation?
30916      */   
30917     isLayoutInstant : false, // needed?
30918    
30919     /**
30920      * @cfg {Number} boxWidth  width of the columns
30921      */   
30922     boxWidth : 450,
30923     
30924       /**
30925      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30926      */   
30927     boxHeight : 0,
30928     
30929     /**
30930      * @cfg {Number} padWidth padding below box..
30931      */   
30932     padWidth : 10, 
30933     
30934     /**
30935      * @cfg {Number} gutter gutter width..
30936      */   
30937     gutter : 10,
30938     
30939      /**
30940      * @cfg {Number} maxCols maximum number of columns
30941      */   
30942     
30943     maxCols: 0,
30944     
30945     /**
30946      * @cfg {Boolean} isAutoInitial defalut true
30947      */   
30948     isAutoInitial : true, 
30949     
30950     containerWidth: 0,
30951     
30952     /**
30953      * @cfg {Boolean} isHorizontal defalut false
30954      */   
30955     isHorizontal : false, 
30956
30957     currentSize : null,
30958     
30959     tag: 'div',
30960     
30961     cls: '',
30962     
30963     bricks: null, //CompositeElement
30964     
30965     cols : 1,
30966     
30967     _isLayoutInited : false,
30968     
30969 //    isAlternative : false, // only use for vertical layout...
30970     
30971     /**
30972      * @cfg {Number} alternativePadWidth padding below box..
30973      */   
30974     alternativePadWidth : 50,
30975     
30976     selectedBrick : [],
30977     
30978     getAutoCreate : function(){
30979         
30980         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30981         
30982         var cfg = {
30983             tag: this.tag,
30984             cls: 'blog-masonary-wrapper ' + this.cls,
30985             cn : {
30986                 cls : 'mas-boxes masonary'
30987             }
30988         };
30989         
30990         return cfg;
30991     },
30992     
30993     getChildContainer: function( )
30994     {
30995         if (this.boxesEl) {
30996             return this.boxesEl;
30997         }
30998         
30999         this.boxesEl = this.el.select('.mas-boxes').first();
31000         
31001         return this.boxesEl;
31002     },
31003     
31004     
31005     initEvents : function()
31006     {
31007         var _this = this;
31008         
31009         if(this.isAutoInitial){
31010             Roo.log('hook children rendered');
31011             this.on('childrenrendered', function() {
31012                 Roo.log('children rendered');
31013                 _this.initial();
31014             } ,this);
31015         }
31016     },
31017     
31018     initial : function()
31019     {
31020         this.selectedBrick = [];
31021         
31022         this.currentSize = this.el.getBox(true);
31023         
31024         Roo.EventManager.onWindowResize(this.resize, this); 
31025
31026         if(!this.isAutoInitial){
31027             this.layout();
31028             return;
31029         }
31030         
31031         this.layout();
31032         
31033         return;
31034         //this.layout.defer(500,this);
31035         
31036     },
31037     
31038     resize : function()
31039     {
31040         var cs = this.el.getBox(true);
31041         
31042         if (
31043                 this.currentSize.width == cs.width && 
31044                 this.currentSize.x == cs.x && 
31045                 this.currentSize.height == cs.height && 
31046                 this.currentSize.y == cs.y 
31047         ) {
31048             Roo.log("no change in with or X or Y");
31049             return;
31050         }
31051         
31052         this.currentSize = cs;
31053         
31054         this.layout();
31055         
31056     },
31057     
31058     layout : function()
31059     {   
31060         this._resetLayout();
31061         
31062         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31063         
31064         this.layoutItems( isInstant );
31065       
31066         this._isLayoutInited = true;
31067         
31068         this.fireEvent('layout', this);
31069         
31070     },
31071     
31072     _resetLayout : function()
31073     {
31074         if(this.isHorizontal){
31075             this.horizontalMeasureColumns();
31076             return;
31077         }
31078         
31079         this.verticalMeasureColumns();
31080         
31081     },
31082     
31083     verticalMeasureColumns : function()
31084     {
31085         this.getContainerWidth();
31086         
31087 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31088 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31089 //            return;
31090 //        }
31091         
31092         var boxWidth = this.boxWidth + this.padWidth;
31093         
31094         if(this.containerWidth < this.boxWidth){
31095             boxWidth = this.containerWidth
31096         }
31097         
31098         var containerWidth = this.containerWidth;
31099         
31100         var cols = Math.floor(containerWidth / boxWidth);
31101         
31102         this.cols = Math.max( cols, 1 );
31103         
31104         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31105         
31106         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31107         
31108         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31109         
31110         this.colWidth = boxWidth + avail - this.padWidth;
31111         
31112         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31113         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31114     },
31115     
31116     horizontalMeasureColumns : function()
31117     {
31118         this.getContainerWidth();
31119         
31120         var boxWidth = this.boxWidth;
31121         
31122         if(this.containerWidth < boxWidth){
31123             boxWidth = this.containerWidth;
31124         }
31125         
31126         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31127         
31128         this.el.setHeight(boxWidth);
31129         
31130     },
31131     
31132     getContainerWidth : function()
31133     {
31134         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31135     },
31136     
31137     layoutItems : function( isInstant )
31138     {
31139         Roo.log(this.bricks);
31140         
31141         var items = Roo.apply([], this.bricks);
31142         
31143         if(this.isHorizontal){
31144             this._horizontalLayoutItems( items , isInstant );
31145             return;
31146         }
31147         
31148 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31149 //            this._verticalAlternativeLayoutItems( items , isInstant );
31150 //            return;
31151 //        }
31152         
31153         this._verticalLayoutItems( items , isInstant );
31154         
31155     },
31156     
31157     _verticalLayoutItems : function ( items , isInstant)
31158     {
31159         if ( !items || !items.length ) {
31160             return;
31161         }
31162         
31163         var standard = [
31164             ['xs', 'xs', 'xs', 'tall'],
31165             ['xs', 'xs', 'tall'],
31166             ['xs', 'xs', 'sm'],
31167             ['xs', 'xs', 'xs'],
31168             ['xs', 'tall'],
31169             ['xs', 'sm'],
31170             ['xs', 'xs'],
31171             ['xs'],
31172             
31173             ['sm', 'xs', 'xs'],
31174             ['sm', 'xs'],
31175             ['sm'],
31176             
31177             ['tall', 'xs', 'xs', 'xs'],
31178             ['tall', 'xs', 'xs'],
31179             ['tall', 'xs'],
31180             ['tall']
31181             
31182         ];
31183         
31184         var queue = [];
31185         
31186         var boxes = [];
31187         
31188         var box = [];
31189         
31190         Roo.each(items, function(item, k){
31191             
31192             switch (item.size) {
31193                 // these layouts take up a full box,
31194                 case 'md' :
31195                 case 'md-left' :
31196                 case 'md-right' :
31197                 case 'wide' :
31198                     
31199                     if(box.length){
31200                         boxes.push(box);
31201                         box = [];
31202                     }
31203                     
31204                     boxes.push([item]);
31205                     
31206                     break;
31207                     
31208                 case 'xs' :
31209                 case 'sm' :
31210                 case 'tall' :
31211                     
31212                     box.push(item);
31213                     
31214                     break;
31215                 default :
31216                     break;
31217                     
31218             }
31219             
31220         }, this);
31221         
31222         if(box.length){
31223             boxes.push(box);
31224             box = [];
31225         }
31226         
31227         var filterPattern = function(box, length)
31228         {
31229             if(!box.length){
31230                 return;
31231             }
31232             
31233             var match = false;
31234             
31235             var pattern = box.slice(0, length);
31236             
31237             var format = [];
31238             
31239             Roo.each(pattern, function(i){
31240                 format.push(i.size);
31241             }, this);
31242             
31243             Roo.each(standard, function(s){
31244                 
31245                 if(String(s) != String(format)){
31246                     return;
31247                 }
31248                 
31249                 match = true;
31250                 return false;
31251                 
31252             }, this);
31253             
31254             if(!match && length == 1){
31255                 return;
31256             }
31257             
31258             if(!match){
31259                 filterPattern(box, length - 1);
31260                 return;
31261             }
31262                 
31263             queue.push(pattern);
31264
31265             box = box.slice(length, box.length);
31266
31267             filterPattern(box, 4);
31268
31269             return;
31270             
31271         }
31272         
31273         Roo.each(boxes, function(box, k){
31274             
31275             if(!box.length){
31276                 return;
31277             }
31278             
31279             if(box.length == 1){
31280                 queue.push(box);
31281                 return;
31282             }
31283             
31284             filterPattern(box, 4);
31285             
31286         }, this);
31287         
31288         this._processVerticalLayoutQueue( queue, isInstant );
31289         
31290     },
31291     
31292 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31293 //    {
31294 //        if ( !items || !items.length ) {
31295 //            return;
31296 //        }
31297 //
31298 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31299 //        
31300 //    },
31301     
31302     _horizontalLayoutItems : function ( items , isInstant)
31303     {
31304         if ( !items || !items.length || items.length < 3) {
31305             return;
31306         }
31307         
31308         items.reverse();
31309         
31310         var eItems = items.slice(0, 3);
31311         
31312         items = items.slice(3, items.length);
31313         
31314         var standard = [
31315             ['xs', 'xs', 'xs', 'wide'],
31316             ['xs', 'xs', 'wide'],
31317             ['xs', 'xs', 'sm'],
31318             ['xs', 'xs', 'xs'],
31319             ['xs', 'wide'],
31320             ['xs', 'sm'],
31321             ['xs', 'xs'],
31322             ['xs'],
31323             
31324             ['sm', 'xs', 'xs'],
31325             ['sm', 'xs'],
31326             ['sm'],
31327             
31328             ['wide', 'xs', 'xs', 'xs'],
31329             ['wide', 'xs', 'xs'],
31330             ['wide', 'xs'],
31331             ['wide'],
31332             
31333             ['wide-thin']
31334         ];
31335         
31336         var queue = [];
31337         
31338         var boxes = [];
31339         
31340         var box = [];
31341         
31342         Roo.each(items, function(item, k){
31343             
31344             switch (item.size) {
31345                 case 'md' :
31346                 case 'md-left' :
31347                 case 'md-right' :
31348                 case 'tall' :
31349                     
31350                     if(box.length){
31351                         boxes.push(box);
31352                         box = [];
31353                     }
31354                     
31355                     boxes.push([item]);
31356                     
31357                     break;
31358                     
31359                 case 'xs' :
31360                 case 'sm' :
31361                 case 'wide' :
31362                 case 'wide-thin' :
31363                     
31364                     box.push(item);
31365                     
31366                     break;
31367                 default :
31368                     break;
31369                     
31370             }
31371             
31372         }, this);
31373         
31374         if(box.length){
31375             boxes.push(box);
31376             box = [];
31377         }
31378         
31379         var filterPattern = function(box, length)
31380         {
31381             if(!box.length){
31382                 return;
31383             }
31384             
31385             var match = false;
31386             
31387             var pattern = box.slice(0, length);
31388             
31389             var format = [];
31390             
31391             Roo.each(pattern, function(i){
31392                 format.push(i.size);
31393             }, this);
31394             
31395             Roo.each(standard, function(s){
31396                 
31397                 if(String(s) != String(format)){
31398                     return;
31399                 }
31400                 
31401                 match = true;
31402                 return false;
31403                 
31404             }, this);
31405             
31406             if(!match && length == 1){
31407                 return;
31408             }
31409             
31410             if(!match){
31411                 filterPattern(box, length - 1);
31412                 return;
31413             }
31414                 
31415             queue.push(pattern);
31416
31417             box = box.slice(length, box.length);
31418
31419             filterPattern(box, 4);
31420
31421             return;
31422             
31423         }
31424         
31425         Roo.each(boxes, function(box, k){
31426             
31427             if(!box.length){
31428                 return;
31429             }
31430             
31431             if(box.length == 1){
31432                 queue.push(box);
31433                 return;
31434             }
31435             
31436             filterPattern(box, 4);
31437             
31438         }, this);
31439         
31440         
31441         var prune = [];
31442         
31443         var pos = this.el.getBox(true);
31444         
31445         var minX = pos.x;
31446         
31447         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31448         
31449         var hit_end = false;
31450         
31451         Roo.each(queue, function(box){
31452             
31453             if(hit_end){
31454                 
31455                 Roo.each(box, function(b){
31456                 
31457                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31458                     b.el.hide();
31459
31460                 }, this);
31461
31462                 return;
31463             }
31464             
31465             var mx = 0;
31466             
31467             Roo.each(box, function(b){
31468                 
31469                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31470                 b.el.show();
31471
31472                 mx = Math.max(mx, b.x);
31473                 
31474             }, this);
31475             
31476             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31477             
31478             if(maxX < minX){
31479                 
31480                 Roo.each(box, function(b){
31481                 
31482                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31483                     b.el.hide();
31484                     
31485                 }, this);
31486                 
31487                 hit_end = true;
31488                 
31489                 return;
31490             }
31491             
31492             prune.push(box);
31493             
31494         }, this);
31495         
31496         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31497     },
31498     
31499     /** Sets position of item in DOM
31500     * @param {Element} item
31501     * @param {Number} x - horizontal position
31502     * @param {Number} y - vertical position
31503     * @param {Boolean} isInstant - disables transitions
31504     */
31505     _processVerticalLayoutQueue : function( queue, isInstant )
31506     {
31507         var pos = this.el.getBox(true);
31508         var x = pos.x;
31509         var y = pos.y;
31510         var maxY = [];
31511         
31512         for (var i = 0; i < this.cols; i++){
31513             maxY[i] = pos.y;
31514         }
31515         
31516         Roo.each(queue, function(box, k){
31517             
31518             var col = k % this.cols;
31519             
31520             Roo.each(box, function(b,kk){
31521                 
31522                 b.el.position('absolute');
31523                 
31524                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31525                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31526                 
31527                 if(b.size == 'md-left' || b.size == 'md-right'){
31528                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31529                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31530                 }
31531                 
31532                 b.el.setWidth(width);
31533                 b.el.setHeight(height);
31534                 // iframe?
31535                 b.el.select('iframe',true).setSize(width,height);
31536                 
31537             }, this);
31538             
31539             for (var i = 0; i < this.cols; i++){
31540                 
31541                 if(maxY[i] < maxY[col]){
31542                     col = i;
31543                     continue;
31544                 }
31545                 
31546                 col = Math.min(col, i);
31547                 
31548             }
31549             
31550             x = pos.x + col * (this.colWidth + this.padWidth);
31551             
31552             y = maxY[col];
31553             
31554             var positions = [];
31555             
31556             switch (box.length){
31557                 case 1 :
31558                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31559                     break;
31560                 case 2 :
31561                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31562                     break;
31563                 case 3 :
31564                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31565                     break;
31566                 case 4 :
31567                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31568                     break;
31569                 default :
31570                     break;
31571             }
31572             
31573             Roo.each(box, function(b,kk){
31574                 
31575                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31576                 
31577                 var sz = b.el.getSize();
31578                 
31579                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31580                 
31581             }, this);
31582             
31583         }, this);
31584         
31585         var mY = 0;
31586         
31587         for (var i = 0; i < this.cols; i++){
31588             mY = Math.max(mY, maxY[i]);
31589         }
31590         
31591         this.el.setHeight(mY - pos.y);
31592         
31593     },
31594     
31595 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31596 //    {
31597 //        var pos = this.el.getBox(true);
31598 //        var x = pos.x;
31599 //        var y = pos.y;
31600 //        var maxX = pos.right;
31601 //        
31602 //        var maxHeight = 0;
31603 //        
31604 //        Roo.each(items, function(item, k){
31605 //            
31606 //            var c = k % 2;
31607 //            
31608 //            item.el.position('absolute');
31609 //                
31610 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31611 //
31612 //            item.el.setWidth(width);
31613 //
31614 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31615 //
31616 //            item.el.setHeight(height);
31617 //            
31618 //            if(c == 0){
31619 //                item.el.setXY([x, y], isInstant ? false : true);
31620 //            } else {
31621 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31622 //            }
31623 //            
31624 //            y = y + height + this.alternativePadWidth;
31625 //            
31626 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31627 //            
31628 //        }, this);
31629 //        
31630 //        this.el.setHeight(maxHeight);
31631 //        
31632 //    },
31633     
31634     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31635     {
31636         var pos = this.el.getBox(true);
31637         
31638         var minX = pos.x;
31639         var minY = pos.y;
31640         
31641         var maxX = pos.right;
31642         
31643         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31644         
31645         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31646         
31647         Roo.each(queue, function(box, k){
31648             
31649             Roo.each(box, function(b, kk){
31650                 
31651                 b.el.position('absolute');
31652                 
31653                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31654                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31655                 
31656                 if(b.size == 'md-left' || b.size == 'md-right'){
31657                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31658                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31659                 }
31660                 
31661                 b.el.setWidth(width);
31662                 b.el.setHeight(height);
31663                 
31664             }, this);
31665             
31666             if(!box.length){
31667                 return;
31668             }
31669             
31670             var positions = [];
31671             
31672             switch (box.length){
31673                 case 1 :
31674                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31675                     break;
31676                 case 2 :
31677                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31678                     break;
31679                 case 3 :
31680                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31681                     break;
31682                 case 4 :
31683                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31684                     break;
31685                 default :
31686                     break;
31687             }
31688             
31689             Roo.each(box, function(b,kk){
31690                 
31691                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31692                 
31693                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31694                 
31695             }, this);
31696             
31697         }, this);
31698         
31699     },
31700     
31701     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31702     {
31703         Roo.each(eItems, function(b,k){
31704             
31705             b.size = (k == 0) ? 'sm' : 'xs';
31706             b.x = (k == 0) ? 2 : 1;
31707             b.y = (k == 0) ? 2 : 1;
31708             
31709             b.el.position('absolute');
31710             
31711             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31712                 
31713             b.el.setWidth(width);
31714             
31715             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31716             
31717             b.el.setHeight(height);
31718             
31719         }, this);
31720
31721         var positions = [];
31722         
31723         positions.push({
31724             x : maxX - this.unitWidth * 2 - this.gutter,
31725             y : minY
31726         });
31727         
31728         positions.push({
31729             x : maxX - this.unitWidth,
31730             y : minY + (this.unitWidth + this.gutter) * 2
31731         });
31732         
31733         positions.push({
31734             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31735             y : minY
31736         });
31737         
31738         Roo.each(eItems, function(b,k){
31739             
31740             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31741
31742         }, this);
31743         
31744     },
31745     
31746     getVerticalOneBoxColPositions : function(x, y, box)
31747     {
31748         var pos = [];
31749         
31750         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31751         
31752         if(box[0].size == 'md-left'){
31753             rand = 0;
31754         }
31755         
31756         if(box[0].size == 'md-right'){
31757             rand = 1;
31758         }
31759         
31760         pos.push({
31761             x : x + (this.unitWidth + this.gutter) * rand,
31762             y : y
31763         });
31764         
31765         return pos;
31766     },
31767     
31768     getVerticalTwoBoxColPositions : function(x, y, box)
31769     {
31770         var pos = [];
31771         
31772         if(box[0].size == 'xs'){
31773             
31774             pos.push({
31775                 x : x,
31776                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31777             });
31778
31779             pos.push({
31780                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31781                 y : y
31782             });
31783             
31784             return pos;
31785             
31786         }
31787         
31788         pos.push({
31789             x : x,
31790             y : y
31791         });
31792
31793         pos.push({
31794             x : x + (this.unitWidth + this.gutter) * 2,
31795             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31796         });
31797         
31798         return pos;
31799         
31800     },
31801     
31802     getVerticalThreeBoxColPositions : function(x, y, box)
31803     {
31804         var pos = [];
31805         
31806         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31807             
31808             pos.push({
31809                 x : x,
31810                 y : y
31811             });
31812
31813             pos.push({
31814                 x : x + (this.unitWidth + this.gutter) * 1,
31815                 y : y
31816             });
31817             
31818             pos.push({
31819                 x : x + (this.unitWidth + this.gutter) * 2,
31820                 y : y
31821             });
31822             
31823             return pos;
31824             
31825         }
31826         
31827         if(box[0].size == 'xs' && box[1].size == 'xs'){
31828             
31829             pos.push({
31830                 x : x,
31831                 y : y
31832             });
31833
31834             pos.push({
31835                 x : x,
31836                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31837             });
31838             
31839             pos.push({
31840                 x : x + (this.unitWidth + this.gutter) * 1,
31841                 y : y
31842             });
31843             
31844             return pos;
31845             
31846         }
31847         
31848         pos.push({
31849             x : x,
31850             y : y
31851         });
31852
31853         pos.push({
31854             x : x + (this.unitWidth + this.gutter) * 2,
31855             y : y
31856         });
31857
31858         pos.push({
31859             x : x + (this.unitWidth + this.gutter) * 2,
31860             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31861         });
31862             
31863         return pos;
31864         
31865     },
31866     
31867     getVerticalFourBoxColPositions : function(x, y, box)
31868     {
31869         var pos = [];
31870         
31871         if(box[0].size == 'xs'){
31872             
31873             pos.push({
31874                 x : x,
31875                 y : y
31876             });
31877
31878             pos.push({
31879                 x : x,
31880                 y : y + (this.unitHeight + this.gutter) * 1
31881             });
31882             
31883             pos.push({
31884                 x : x,
31885                 y : y + (this.unitHeight + this.gutter) * 2
31886             });
31887             
31888             pos.push({
31889                 x : x + (this.unitWidth + this.gutter) * 1,
31890                 y : y
31891             });
31892             
31893             return pos;
31894             
31895         }
31896         
31897         pos.push({
31898             x : x,
31899             y : y
31900         });
31901
31902         pos.push({
31903             x : x + (this.unitWidth + this.gutter) * 2,
31904             y : y
31905         });
31906
31907         pos.push({
31908             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31909             y : y + (this.unitHeight + this.gutter) * 1
31910         });
31911
31912         pos.push({
31913             x : x + (this.unitWidth + this.gutter) * 2,
31914             y : y + (this.unitWidth + this.gutter) * 2
31915         });
31916
31917         return pos;
31918         
31919     },
31920     
31921     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31922     {
31923         var pos = [];
31924         
31925         if(box[0].size == 'md-left'){
31926             pos.push({
31927                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31928                 y : minY
31929             });
31930             
31931             return pos;
31932         }
31933         
31934         if(box[0].size == 'md-right'){
31935             pos.push({
31936                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31937                 y : minY + (this.unitWidth + this.gutter) * 1
31938             });
31939             
31940             return pos;
31941         }
31942         
31943         var rand = Math.floor(Math.random() * (4 - box[0].y));
31944         
31945         pos.push({
31946             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31947             y : minY + (this.unitWidth + this.gutter) * rand
31948         });
31949         
31950         return pos;
31951         
31952     },
31953     
31954     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31955     {
31956         var pos = [];
31957         
31958         if(box[0].size == 'xs'){
31959             
31960             pos.push({
31961                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31962                 y : minY
31963             });
31964
31965             pos.push({
31966                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31967                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31968             });
31969             
31970             return pos;
31971             
31972         }
31973         
31974         pos.push({
31975             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31976             y : minY
31977         });
31978
31979         pos.push({
31980             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31981             y : minY + (this.unitWidth + this.gutter) * 2
31982         });
31983         
31984         return pos;
31985         
31986     },
31987     
31988     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31989     {
31990         var pos = [];
31991         
31992         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31993             
31994             pos.push({
31995                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31996                 y : minY
31997             });
31998
31999             pos.push({
32000                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32001                 y : minY + (this.unitWidth + this.gutter) * 1
32002             });
32003             
32004             pos.push({
32005                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32006                 y : minY + (this.unitWidth + this.gutter) * 2
32007             });
32008             
32009             return pos;
32010             
32011         }
32012         
32013         if(box[0].size == 'xs' && box[1].size == 'xs'){
32014             
32015             pos.push({
32016                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32017                 y : minY
32018             });
32019
32020             pos.push({
32021                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32022                 y : minY
32023             });
32024             
32025             pos.push({
32026                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32027                 y : minY + (this.unitWidth + this.gutter) * 1
32028             });
32029             
32030             return pos;
32031             
32032         }
32033         
32034         pos.push({
32035             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32036             y : minY
32037         });
32038
32039         pos.push({
32040             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32041             y : minY + (this.unitWidth + this.gutter) * 2
32042         });
32043
32044         pos.push({
32045             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32046             y : minY + (this.unitWidth + this.gutter) * 2
32047         });
32048             
32049         return pos;
32050         
32051     },
32052     
32053     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32054     {
32055         var pos = [];
32056         
32057         if(box[0].size == 'xs'){
32058             
32059             pos.push({
32060                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32061                 y : minY
32062             });
32063
32064             pos.push({
32065                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32066                 y : minY
32067             });
32068             
32069             pos.push({
32070                 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),
32071                 y : minY
32072             });
32073             
32074             pos.push({
32075                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32076                 y : minY + (this.unitWidth + this.gutter) * 1
32077             });
32078             
32079             return pos;
32080             
32081         }
32082         
32083         pos.push({
32084             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32085             y : minY
32086         });
32087         
32088         pos.push({
32089             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32090             y : minY + (this.unitWidth + this.gutter) * 2
32091         });
32092         
32093         pos.push({
32094             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32095             y : minY + (this.unitWidth + this.gutter) * 2
32096         });
32097         
32098         pos.push({
32099             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),
32100             y : minY + (this.unitWidth + this.gutter) * 2
32101         });
32102
32103         return pos;
32104         
32105     },
32106     
32107     /**
32108     * remove a Masonry Brick
32109     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32110     */
32111     removeBrick : function(brick_id)
32112     {
32113         if (!brick_id) {
32114             return;
32115         }
32116         
32117         for (var i = 0; i<this.bricks.length; i++) {
32118             if (this.bricks[i].id == brick_id) {
32119                 this.bricks.splice(i,1);
32120                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32121                 this.initial();
32122             }
32123         }
32124     },
32125     
32126     /**
32127     * adds a Masonry Brick
32128     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32129     */
32130     addBrick : function(cfg)
32131     {
32132         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32133         //this.register(cn);
32134         cn.parentId = this.id;
32135         cn.render(this.el);
32136         return cn;
32137     },
32138     
32139     /**
32140     * register a Masonry Brick
32141     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32142     */
32143     
32144     register : function(brick)
32145     {
32146         this.bricks.push(brick);
32147         brick.masonryId = this.id;
32148     },
32149     
32150     /**
32151     * clear all the Masonry Brick
32152     */
32153     clearAll : function()
32154     {
32155         this.bricks = [];
32156         //this.getChildContainer().dom.innerHTML = "";
32157         this.el.dom.innerHTML = '';
32158     },
32159     
32160     getSelected : function()
32161     {
32162         if (!this.selectedBrick) {
32163             return false;
32164         }
32165         
32166         return this.selectedBrick;
32167     }
32168 });
32169
32170 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32171     
32172     groups: {},
32173      /**
32174     * register a Masonry Layout
32175     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32176     */
32177     
32178     register : function(layout)
32179     {
32180         this.groups[layout.id] = layout;
32181     },
32182     /**
32183     * fetch a  Masonry Layout based on the masonry layout ID
32184     * @param {string} the masonry layout to add
32185     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32186     */
32187     
32188     get: function(layout_id) {
32189         if (typeof(this.groups[layout_id]) == 'undefined') {
32190             return false;
32191         }
32192         return this.groups[layout_id] ;
32193     }
32194     
32195     
32196     
32197 });
32198
32199  
32200
32201  /**
32202  *
32203  * This is based on 
32204  * http://masonry.desandro.com
32205  *
32206  * The idea is to render all the bricks based on vertical width...
32207  *
32208  * The original code extends 'outlayer' - we might need to use that....
32209  * 
32210  */
32211
32212
32213 /**
32214  * @class Roo.bootstrap.LayoutMasonryAuto
32215  * @extends Roo.bootstrap.Component
32216  * Bootstrap Layout Masonry class
32217  * 
32218  * @constructor
32219  * Create a new Element
32220  * @param {Object} config The config object
32221  */
32222
32223 Roo.bootstrap.LayoutMasonryAuto = function(config){
32224     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32225 };
32226
32227 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32228     
32229       /**
32230      * @cfg {Boolean} isFitWidth  - resize the width..
32231      */   
32232     isFitWidth : false,  // options..
32233     /**
32234      * @cfg {Boolean} isOriginLeft = left align?
32235      */   
32236     isOriginLeft : true,
32237     /**
32238      * @cfg {Boolean} isOriginTop = top align?
32239      */   
32240     isOriginTop : false,
32241     /**
32242      * @cfg {Boolean} isLayoutInstant = no animation?
32243      */   
32244     isLayoutInstant : false, // needed?
32245     /**
32246      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32247      */   
32248     isResizingContainer : true,
32249     /**
32250      * @cfg {Number} columnWidth  width of the columns 
32251      */   
32252     
32253     columnWidth : 0,
32254     
32255     /**
32256      * @cfg {Number} maxCols maximum number of columns
32257      */   
32258     
32259     maxCols: 0,
32260     /**
32261      * @cfg {Number} padHeight padding below box..
32262      */   
32263     
32264     padHeight : 10, 
32265     
32266     /**
32267      * @cfg {Boolean} isAutoInitial defalut true
32268      */   
32269     
32270     isAutoInitial : true, 
32271     
32272     // private?
32273     gutter : 0,
32274     
32275     containerWidth: 0,
32276     initialColumnWidth : 0,
32277     currentSize : null,
32278     
32279     colYs : null, // array.
32280     maxY : 0,
32281     padWidth: 10,
32282     
32283     
32284     tag: 'div',
32285     cls: '',
32286     bricks: null, //CompositeElement
32287     cols : 0, // array?
32288     // element : null, // wrapped now this.el
32289     _isLayoutInited : null, 
32290     
32291     
32292     getAutoCreate : function(){
32293         
32294         var cfg = {
32295             tag: this.tag,
32296             cls: 'blog-masonary-wrapper ' + this.cls,
32297             cn : {
32298                 cls : 'mas-boxes masonary'
32299             }
32300         };
32301         
32302         return cfg;
32303     },
32304     
32305     getChildContainer: function( )
32306     {
32307         if (this.boxesEl) {
32308             return this.boxesEl;
32309         }
32310         
32311         this.boxesEl = this.el.select('.mas-boxes').first();
32312         
32313         return this.boxesEl;
32314     },
32315     
32316     
32317     initEvents : function()
32318     {
32319         var _this = this;
32320         
32321         if(this.isAutoInitial){
32322             Roo.log('hook children rendered');
32323             this.on('childrenrendered', function() {
32324                 Roo.log('children rendered');
32325                 _this.initial();
32326             } ,this);
32327         }
32328         
32329     },
32330     
32331     initial : function()
32332     {
32333         this.reloadItems();
32334
32335         this.currentSize = this.el.getBox(true);
32336
32337         /// was window resize... - let's see if this works..
32338         Roo.EventManager.onWindowResize(this.resize, this); 
32339
32340         if(!this.isAutoInitial){
32341             this.layout();
32342             return;
32343         }
32344         
32345         this.layout.defer(500,this);
32346     },
32347     
32348     reloadItems: function()
32349     {
32350         this.bricks = this.el.select('.masonry-brick', true);
32351         
32352         this.bricks.each(function(b) {
32353             //Roo.log(b.getSize());
32354             if (!b.attr('originalwidth')) {
32355                 b.attr('originalwidth',  b.getSize().width);
32356             }
32357             
32358         });
32359         
32360         Roo.log(this.bricks.elements.length);
32361     },
32362     
32363     resize : function()
32364     {
32365         Roo.log('resize');
32366         var cs = this.el.getBox(true);
32367         
32368         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32369             Roo.log("no change in with or X");
32370             return;
32371         }
32372         this.currentSize = cs;
32373         this.layout();
32374     },
32375     
32376     layout : function()
32377     {
32378          Roo.log('layout');
32379         this._resetLayout();
32380         //this._manageStamps();
32381       
32382         // don't animate first layout
32383         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32384         this.layoutItems( isInstant );
32385       
32386         // flag for initalized
32387         this._isLayoutInited = true;
32388     },
32389     
32390     layoutItems : function( isInstant )
32391     {
32392         //var items = this._getItemsForLayout( this.items );
32393         // original code supports filtering layout items.. we just ignore it..
32394         
32395         this._layoutItems( this.bricks , isInstant );
32396       
32397         this._postLayout();
32398     },
32399     _layoutItems : function ( items , isInstant)
32400     {
32401        //this.fireEvent( 'layout', this, items );
32402     
32403
32404         if ( !items || !items.elements.length ) {
32405           // no items, emit event with empty array
32406             return;
32407         }
32408
32409         var queue = [];
32410         items.each(function(item) {
32411             Roo.log("layout item");
32412             Roo.log(item);
32413             // get x/y object from method
32414             var position = this._getItemLayoutPosition( item );
32415             // enqueue
32416             position.item = item;
32417             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32418             queue.push( position );
32419         }, this);
32420       
32421         this._processLayoutQueue( queue );
32422     },
32423     /** Sets position of item in DOM
32424     * @param {Element} item
32425     * @param {Number} x - horizontal position
32426     * @param {Number} y - vertical position
32427     * @param {Boolean} isInstant - disables transitions
32428     */
32429     _processLayoutQueue : function( queue )
32430     {
32431         for ( var i=0, len = queue.length; i < len; i++ ) {
32432             var obj = queue[i];
32433             obj.item.position('absolute');
32434             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32435         }
32436     },
32437       
32438     
32439     /**
32440     * Any logic you want to do after each layout,
32441     * i.e. size the container
32442     */
32443     _postLayout : function()
32444     {
32445         this.resizeContainer();
32446     },
32447     
32448     resizeContainer : function()
32449     {
32450         if ( !this.isResizingContainer ) {
32451             return;
32452         }
32453         var size = this._getContainerSize();
32454         if ( size ) {
32455             this.el.setSize(size.width,size.height);
32456             this.boxesEl.setSize(size.width,size.height);
32457         }
32458     },
32459     
32460     
32461     
32462     _resetLayout : function()
32463     {
32464         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32465         this.colWidth = this.el.getWidth();
32466         //this.gutter = this.el.getWidth(); 
32467         
32468         this.measureColumns();
32469
32470         // reset column Y
32471         var i = this.cols;
32472         this.colYs = [];
32473         while (i--) {
32474             this.colYs.push( 0 );
32475         }
32476     
32477         this.maxY = 0;
32478     },
32479
32480     measureColumns : function()
32481     {
32482         this.getContainerWidth();
32483       // if columnWidth is 0, default to outerWidth of first item
32484         if ( !this.columnWidth ) {
32485             var firstItem = this.bricks.first();
32486             Roo.log(firstItem);
32487             this.columnWidth  = this.containerWidth;
32488             if (firstItem && firstItem.attr('originalwidth') ) {
32489                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32490             }
32491             // columnWidth fall back to item of first element
32492             Roo.log("set column width?");
32493                         this.initialColumnWidth = this.columnWidth  ;
32494
32495             // if first elem has no width, default to size of container
32496             
32497         }
32498         
32499         
32500         if (this.initialColumnWidth) {
32501             this.columnWidth = this.initialColumnWidth;
32502         }
32503         
32504         
32505             
32506         // column width is fixed at the top - however if container width get's smaller we should
32507         // reduce it...
32508         
32509         // this bit calcs how man columns..
32510             
32511         var columnWidth = this.columnWidth += this.gutter;
32512       
32513         // calculate columns
32514         var containerWidth = this.containerWidth + this.gutter;
32515         
32516         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32517         // fix rounding errors, typically with gutters
32518         var excess = columnWidth - containerWidth % columnWidth;
32519         
32520         
32521         // if overshoot is less than a pixel, round up, otherwise floor it
32522         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32523         cols = Math[ mathMethod ]( cols );
32524         this.cols = Math.max( cols, 1 );
32525         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32526         
32527          // padding positioning..
32528         var totalColWidth = this.cols * this.columnWidth;
32529         var padavail = this.containerWidth - totalColWidth;
32530         // so for 2 columns - we need 3 'pads'
32531         
32532         var padNeeded = (1+this.cols) * this.padWidth;
32533         
32534         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32535         
32536         this.columnWidth += padExtra
32537         //this.padWidth = Math.floor(padavail /  ( this.cols));
32538         
32539         // adjust colum width so that padding is fixed??
32540         
32541         // we have 3 columns ... total = width * 3
32542         // we have X left over... that should be used by 
32543         
32544         //if (this.expandC) {
32545             
32546         //}
32547         
32548         
32549         
32550     },
32551     
32552     getContainerWidth : function()
32553     {
32554        /* // container is parent if fit width
32555         var container = this.isFitWidth ? this.element.parentNode : this.element;
32556         // check that this.size and size are there
32557         // IE8 triggers resize on body size change, so they might not be
32558         
32559         var size = getSize( container );  //FIXME
32560         this.containerWidth = size && size.innerWidth; //FIXME
32561         */
32562          
32563         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32564         
32565     },
32566     
32567     _getItemLayoutPosition : function( item )  // what is item?
32568     {
32569         // we resize the item to our columnWidth..
32570       
32571         item.setWidth(this.columnWidth);
32572         item.autoBoxAdjust  = false;
32573         
32574         var sz = item.getSize();
32575  
32576         // how many columns does this brick span
32577         var remainder = this.containerWidth % this.columnWidth;
32578         
32579         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32580         // round if off by 1 pixel, otherwise use ceil
32581         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32582         colSpan = Math.min( colSpan, this.cols );
32583         
32584         // normally this should be '1' as we dont' currently allow multi width columns..
32585         
32586         var colGroup = this._getColGroup( colSpan );
32587         // get the minimum Y value from the columns
32588         var minimumY = Math.min.apply( Math, colGroup );
32589         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32590         
32591         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32592          
32593         // position the brick
32594         var position = {
32595             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32596             y: this.currentSize.y + minimumY + this.padHeight
32597         };
32598         
32599         Roo.log(position);
32600         // apply setHeight to necessary columns
32601         var setHeight = minimumY + sz.height + this.padHeight;
32602         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32603         
32604         var setSpan = this.cols + 1 - colGroup.length;
32605         for ( var i = 0; i < setSpan; i++ ) {
32606           this.colYs[ shortColIndex + i ] = setHeight ;
32607         }
32608       
32609         return position;
32610     },
32611     
32612     /**
32613      * @param {Number} colSpan - number of columns the element spans
32614      * @returns {Array} colGroup
32615      */
32616     _getColGroup : function( colSpan )
32617     {
32618         if ( colSpan < 2 ) {
32619           // if brick spans only one column, use all the column Ys
32620           return this.colYs;
32621         }
32622       
32623         var colGroup = [];
32624         // how many different places could this brick fit horizontally
32625         var groupCount = this.cols + 1 - colSpan;
32626         // for each group potential horizontal position
32627         for ( var i = 0; i < groupCount; i++ ) {
32628           // make an array of colY values for that one group
32629           var groupColYs = this.colYs.slice( i, i + colSpan );
32630           // and get the max value of the array
32631           colGroup[i] = Math.max.apply( Math, groupColYs );
32632         }
32633         return colGroup;
32634     },
32635     /*
32636     _manageStamp : function( stamp )
32637     {
32638         var stampSize =  stamp.getSize();
32639         var offset = stamp.getBox();
32640         // get the columns that this stamp affects
32641         var firstX = this.isOriginLeft ? offset.x : offset.right;
32642         var lastX = firstX + stampSize.width;
32643         var firstCol = Math.floor( firstX / this.columnWidth );
32644         firstCol = Math.max( 0, firstCol );
32645         
32646         var lastCol = Math.floor( lastX / this.columnWidth );
32647         // lastCol should not go over if multiple of columnWidth #425
32648         lastCol -= lastX % this.columnWidth ? 0 : 1;
32649         lastCol = Math.min( this.cols - 1, lastCol );
32650         
32651         // set colYs to bottom of the stamp
32652         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32653             stampSize.height;
32654             
32655         for ( var i = firstCol; i <= lastCol; i++ ) {
32656           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32657         }
32658     },
32659     */
32660     
32661     _getContainerSize : function()
32662     {
32663         this.maxY = Math.max.apply( Math, this.colYs );
32664         var size = {
32665             height: this.maxY
32666         };
32667       
32668         if ( this.isFitWidth ) {
32669             size.width = this._getContainerFitWidth();
32670         }
32671       
32672         return size;
32673     },
32674     
32675     _getContainerFitWidth : function()
32676     {
32677         var unusedCols = 0;
32678         // count unused columns
32679         var i = this.cols;
32680         while ( --i ) {
32681           if ( this.colYs[i] !== 0 ) {
32682             break;
32683           }
32684           unusedCols++;
32685         }
32686         // fit container to columns that have been used
32687         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32688     },
32689     
32690     needsResizeLayout : function()
32691     {
32692         var previousWidth = this.containerWidth;
32693         this.getContainerWidth();
32694         return previousWidth !== this.containerWidth;
32695     }
32696  
32697 });
32698
32699  
32700
32701  /*
32702  * - LGPL
32703  *
32704  * element
32705  * 
32706  */
32707
32708 /**
32709  * @class Roo.bootstrap.MasonryBrick
32710  * @extends Roo.bootstrap.Component
32711  * Bootstrap MasonryBrick class
32712  * 
32713  * @constructor
32714  * Create a new MasonryBrick
32715  * @param {Object} config The config object
32716  */
32717
32718 Roo.bootstrap.MasonryBrick = function(config){
32719     
32720     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32721     
32722     Roo.bootstrap.MasonryBrick.register(this);
32723     
32724     this.addEvents({
32725         // raw events
32726         /**
32727          * @event click
32728          * When a MasonryBrick is clcik
32729          * @param {Roo.bootstrap.MasonryBrick} this
32730          * @param {Roo.EventObject} e
32731          */
32732         "click" : true
32733     });
32734 };
32735
32736 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32737     
32738     /**
32739      * @cfg {String} title
32740      */   
32741     title : '',
32742     /**
32743      * @cfg {String} html
32744      */   
32745     html : '',
32746     /**
32747      * @cfg {String} bgimage
32748      */   
32749     bgimage : '',
32750     /**
32751      * @cfg {String} videourl
32752      */   
32753     videourl : '',
32754     /**
32755      * @cfg {String} cls
32756      */   
32757     cls : '',
32758     /**
32759      * @cfg {String} href
32760      */   
32761     href : '',
32762     /**
32763      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32764      */   
32765     size : 'xs',
32766     
32767     /**
32768      * @cfg {String} placetitle (center|bottom)
32769      */   
32770     placetitle : '',
32771     
32772     /**
32773      * @cfg {Boolean} isFitContainer defalut true
32774      */   
32775     isFitContainer : true, 
32776     
32777     /**
32778      * @cfg {Boolean} preventDefault defalut false
32779      */   
32780     preventDefault : false, 
32781     
32782     /**
32783      * @cfg {Boolean} inverse defalut false
32784      */   
32785     maskInverse : false, 
32786     
32787     getAutoCreate : function()
32788     {
32789         if(!this.isFitContainer){
32790             return this.getSplitAutoCreate();
32791         }
32792         
32793         var cls = 'masonry-brick masonry-brick-full';
32794         
32795         if(this.href.length){
32796             cls += ' masonry-brick-link';
32797         }
32798         
32799         if(this.bgimage.length){
32800             cls += ' masonry-brick-image';
32801         }
32802         
32803         if(this.maskInverse){
32804             cls += ' mask-inverse';
32805         }
32806         
32807         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32808             cls += ' enable-mask';
32809         }
32810         
32811         if(this.size){
32812             cls += ' masonry-' + this.size + '-brick';
32813         }
32814         
32815         if(this.placetitle.length){
32816             
32817             switch (this.placetitle) {
32818                 case 'center' :
32819                     cls += ' masonry-center-title';
32820                     break;
32821                 case 'bottom' :
32822                     cls += ' masonry-bottom-title';
32823                     break;
32824                 default:
32825                     break;
32826             }
32827             
32828         } else {
32829             if(!this.html.length && !this.bgimage.length){
32830                 cls += ' masonry-center-title';
32831             }
32832
32833             if(!this.html.length && this.bgimage.length){
32834                 cls += ' masonry-bottom-title';
32835             }
32836         }
32837         
32838         if(this.cls){
32839             cls += ' ' + this.cls;
32840         }
32841         
32842         var cfg = {
32843             tag: (this.href.length) ? 'a' : 'div',
32844             cls: cls,
32845             cn: [
32846                 {
32847                     tag: 'div',
32848                     cls: 'masonry-brick-mask'
32849                 },
32850                 {
32851                     tag: 'div',
32852                     cls: 'masonry-brick-paragraph',
32853                     cn: []
32854                 }
32855             ]
32856         };
32857         
32858         if(this.href.length){
32859             cfg.href = this.href;
32860         }
32861         
32862         var cn = cfg.cn[1].cn;
32863         
32864         if(this.title.length){
32865             cn.push({
32866                 tag: 'h4',
32867                 cls: 'masonry-brick-title',
32868                 html: this.title
32869             });
32870         }
32871         
32872         if(this.html.length){
32873             cn.push({
32874                 tag: 'p',
32875                 cls: 'masonry-brick-text',
32876                 html: this.html
32877             });
32878         }
32879         
32880         if (!this.title.length && !this.html.length) {
32881             cfg.cn[1].cls += ' hide';
32882         }
32883         
32884         if(this.bgimage.length){
32885             cfg.cn.push({
32886                 tag: 'img',
32887                 cls: 'masonry-brick-image-view',
32888                 src: this.bgimage
32889             });
32890         }
32891         
32892         if(this.videourl.length){
32893             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32894             // youtube support only?
32895             cfg.cn.push({
32896                 tag: 'iframe',
32897                 cls: 'masonry-brick-image-view',
32898                 src: vurl,
32899                 frameborder : 0,
32900                 allowfullscreen : true
32901             });
32902         }
32903         
32904         return cfg;
32905         
32906     },
32907     
32908     getSplitAutoCreate : function()
32909     {
32910         var cls = 'masonry-brick masonry-brick-split';
32911         
32912         if(this.href.length){
32913             cls += ' masonry-brick-link';
32914         }
32915         
32916         if(this.bgimage.length){
32917             cls += ' masonry-brick-image';
32918         }
32919         
32920         if(this.size){
32921             cls += ' masonry-' + this.size + '-brick';
32922         }
32923         
32924         switch (this.placetitle) {
32925             case 'center' :
32926                 cls += ' masonry-center-title';
32927                 break;
32928             case 'bottom' :
32929                 cls += ' masonry-bottom-title';
32930                 break;
32931             default:
32932                 if(!this.bgimage.length){
32933                     cls += ' masonry-center-title';
32934                 }
32935
32936                 if(this.bgimage.length){
32937                     cls += ' masonry-bottom-title';
32938                 }
32939                 break;
32940         }
32941         
32942         if(this.cls){
32943             cls += ' ' + this.cls;
32944         }
32945         
32946         var cfg = {
32947             tag: (this.href.length) ? 'a' : 'div',
32948             cls: cls,
32949             cn: [
32950                 {
32951                     tag: 'div',
32952                     cls: 'masonry-brick-split-head',
32953                     cn: [
32954                         {
32955                             tag: 'div',
32956                             cls: 'masonry-brick-paragraph',
32957                             cn: []
32958                         }
32959                     ]
32960                 },
32961                 {
32962                     tag: 'div',
32963                     cls: 'masonry-brick-split-body',
32964                     cn: []
32965                 }
32966             ]
32967         };
32968         
32969         if(this.href.length){
32970             cfg.href = this.href;
32971         }
32972         
32973         if(this.title.length){
32974             cfg.cn[0].cn[0].cn.push({
32975                 tag: 'h4',
32976                 cls: 'masonry-brick-title',
32977                 html: this.title
32978             });
32979         }
32980         
32981         if(this.html.length){
32982             cfg.cn[1].cn.push({
32983                 tag: 'p',
32984                 cls: 'masonry-brick-text',
32985                 html: this.html
32986             });
32987         }
32988
32989         if(this.bgimage.length){
32990             cfg.cn[0].cn.push({
32991                 tag: 'img',
32992                 cls: 'masonry-brick-image-view',
32993                 src: this.bgimage
32994             });
32995         }
32996         
32997         if(this.videourl.length){
32998             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32999             // youtube support only?
33000             cfg.cn[0].cn.cn.push({
33001                 tag: 'iframe',
33002                 cls: 'masonry-brick-image-view',
33003                 src: vurl,
33004                 frameborder : 0,
33005                 allowfullscreen : true
33006             });
33007         }
33008         
33009         return cfg;
33010     },
33011     
33012     initEvents: function() 
33013     {
33014         switch (this.size) {
33015             case 'xs' :
33016                 this.x = 1;
33017                 this.y = 1;
33018                 break;
33019             case 'sm' :
33020                 this.x = 2;
33021                 this.y = 2;
33022                 break;
33023             case 'md' :
33024             case 'md-left' :
33025             case 'md-right' :
33026                 this.x = 3;
33027                 this.y = 3;
33028                 break;
33029             case 'tall' :
33030                 this.x = 2;
33031                 this.y = 3;
33032                 break;
33033             case 'wide' :
33034                 this.x = 3;
33035                 this.y = 2;
33036                 break;
33037             case 'wide-thin' :
33038                 this.x = 3;
33039                 this.y = 1;
33040                 break;
33041                         
33042             default :
33043                 break;
33044         }
33045         
33046         if(Roo.isTouch){
33047             this.el.on('touchstart', this.onTouchStart, this);
33048             this.el.on('touchmove', this.onTouchMove, this);
33049             this.el.on('touchend', this.onTouchEnd, this);
33050             this.el.on('contextmenu', this.onContextMenu, this);
33051         } else {
33052             this.el.on('mouseenter'  ,this.enter, this);
33053             this.el.on('mouseleave', this.leave, this);
33054             this.el.on('click', this.onClick, this);
33055         }
33056         
33057         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33058             this.parent().bricks.push(this);   
33059         }
33060         
33061     },
33062     
33063     onClick: function(e, el)
33064     {
33065         var time = this.endTimer - this.startTimer;
33066         // Roo.log(e.preventDefault());
33067         if(Roo.isTouch){
33068             if(time > 1000){
33069                 e.preventDefault();
33070                 return;
33071             }
33072         }
33073         
33074         if(!this.preventDefault){
33075             return;
33076         }
33077         
33078         e.preventDefault();
33079         
33080         if (this.activeClass != '') {
33081             this.selectBrick();
33082         }
33083         
33084         this.fireEvent('click', this, e);
33085     },
33086     
33087     enter: function(e, el)
33088     {
33089         e.preventDefault();
33090         
33091         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33092             return;
33093         }
33094         
33095         if(this.bgimage.length && this.html.length){
33096             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33097         }
33098     },
33099     
33100     leave: function(e, el)
33101     {
33102         e.preventDefault();
33103         
33104         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33105             return;
33106         }
33107         
33108         if(this.bgimage.length && this.html.length){
33109             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33110         }
33111     },
33112     
33113     onTouchStart: function(e, el)
33114     {
33115 //        e.preventDefault();
33116         
33117         this.touchmoved = false;
33118         
33119         if(!this.isFitContainer){
33120             return;
33121         }
33122         
33123         if(!this.bgimage.length || !this.html.length){
33124             return;
33125         }
33126         
33127         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33128         
33129         this.timer = new Date().getTime();
33130         
33131     },
33132     
33133     onTouchMove: function(e, el)
33134     {
33135         this.touchmoved = true;
33136     },
33137     
33138     onContextMenu : function(e,el)
33139     {
33140         e.preventDefault();
33141         e.stopPropagation();
33142         return false;
33143     },
33144     
33145     onTouchEnd: function(e, el)
33146     {
33147 //        e.preventDefault();
33148         
33149         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33150         
33151             this.leave(e,el);
33152             
33153             return;
33154         }
33155         
33156         if(!this.bgimage.length || !this.html.length){
33157             
33158             if(this.href.length){
33159                 window.location.href = this.href;
33160             }
33161             
33162             return;
33163         }
33164         
33165         if(!this.isFitContainer){
33166             return;
33167         }
33168         
33169         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33170         
33171         window.location.href = this.href;
33172     },
33173     
33174     //selection on single brick only
33175     selectBrick : function() {
33176         
33177         if (!this.parentId) {
33178             return;
33179         }
33180         
33181         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33182         var index = m.selectedBrick.indexOf(this.id);
33183         
33184         if ( index > -1) {
33185             m.selectedBrick.splice(index,1);
33186             this.el.removeClass(this.activeClass);
33187             return;
33188         }
33189         
33190         for(var i = 0; i < m.selectedBrick.length; i++) {
33191             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33192             b.el.removeClass(b.activeClass);
33193         }
33194         
33195         m.selectedBrick = [];
33196         
33197         m.selectedBrick.push(this.id);
33198         this.el.addClass(this.activeClass);
33199         return;
33200     },
33201     
33202     isSelected : function(){
33203         return this.el.hasClass(this.activeClass);
33204         
33205     }
33206 });
33207
33208 Roo.apply(Roo.bootstrap.MasonryBrick, {
33209     
33210     //groups: {},
33211     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33212      /**
33213     * register a Masonry Brick
33214     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33215     */
33216     
33217     register : function(brick)
33218     {
33219         //this.groups[brick.id] = brick;
33220         this.groups.add(brick.id, brick);
33221     },
33222     /**
33223     * fetch a  masonry brick based on the masonry brick ID
33224     * @param {string} the masonry brick to add
33225     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33226     */
33227     
33228     get: function(brick_id) 
33229     {
33230         // if (typeof(this.groups[brick_id]) == 'undefined') {
33231         //     return false;
33232         // }
33233         // return this.groups[brick_id] ;
33234         
33235         if(this.groups.key(brick_id)) {
33236             return this.groups.key(brick_id);
33237         }
33238         
33239         return false;
33240     }
33241     
33242     
33243     
33244 });
33245
33246  /*
33247  * - LGPL
33248  *
33249  * element
33250  * 
33251  */
33252
33253 /**
33254  * @class Roo.bootstrap.Brick
33255  * @extends Roo.bootstrap.Component
33256  * Bootstrap Brick class
33257  * 
33258  * @constructor
33259  * Create a new Brick
33260  * @param {Object} config The config object
33261  */
33262
33263 Roo.bootstrap.Brick = function(config){
33264     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33265     
33266     this.addEvents({
33267         // raw events
33268         /**
33269          * @event click
33270          * When a Brick is click
33271          * @param {Roo.bootstrap.Brick} this
33272          * @param {Roo.EventObject} e
33273          */
33274         "click" : true
33275     });
33276 };
33277
33278 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33279     
33280     /**
33281      * @cfg {String} title
33282      */   
33283     title : '',
33284     /**
33285      * @cfg {String} html
33286      */   
33287     html : '',
33288     /**
33289      * @cfg {String} bgimage
33290      */   
33291     bgimage : '',
33292     /**
33293      * @cfg {String} cls
33294      */   
33295     cls : '',
33296     /**
33297      * @cfg {String} href
33298      */   
33299     href : '',
33300     /**
33301      * @cfg {String} video
33302      */   
33303     video : '',
33304     /**
33305      * @cfg {Boolean} square
33306      */   
33307     square : true,
33308     
33309     getAutoCreate : function()
33310     {
33311         var cls = 'roo-brick';
33312         
33313         if(this.href.length){
33314             cls += ' roo-brick-link';
33315         }
33316         
33317         if(this.bgimage.length){
33318             cls += ' roo-brick-image';
33319         }
33320         
33321         if(!this.html.length && !this.bgimage.length){
33322             cls += ' roo-brick-center-title';
33323         }
33324         
33325         if(!this.html.length && this.bgimage.length){
33326             cls += ' roo-brick-bottom-title';
33327         }
33328         
33329         if(this.cls){
33330             cls += ' ' + this.cls;
33331         }
33332         
33333         var cfg = {
33334             tag: (this.href.length) ? 'a' : 'div',
33335             cls: cls,
33336             cn: [
33337                 {
33338                     tag: 'div',
33339                     cls: 'roo-brick-paragraph',
33340                     cn: []
33341                 }
33342             ]
33343         };
33344         
33345         if(this.href.length){
33346             cfg.href = this.href;
33347         }
33348         
33349         var cn = cfg.cn[0].cn;
33350         
33351         if(this.title.length){
33352             cn.push({
33353                 tag: 'h4',
33354                 cls: 'roo-brick-title',
33355                 html: this.title
33356             });
33357         }
33358         
33359         if(this.html.length){
33360             cn.push({
33361                 tag: 'p',
33362                 cls: 'roo-brick-text',
33363                 html: this.html
33364             });
33365         } else {
33366             cn.cls += ' hide';
33367         }
33368         
33369         if(this.bgimage.length){
33370             cfg.cn.push({
33371                 tag: 'img',
33372                 cls: 'roo-brick-image-view',
33373                 src: this.bgimage
33374             });
33375         }
33376         
33377         return cfg;
33378     },
33379     
33380     initEvents: function() 
33381     {
33382         if(this.title.length || this.html.length){
33383             this.el.on('mouseenter'  ,this.enter, this);
33384             this.el.on('mouseleave', this.leave, this);
33385         }
33386         
33387         Roo.EventManager.onWindowResize(this.resize, this); 
33388         
33389         if(this.bgimage.length){
33390             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33391             this.imageEl.on('load', this.onImageLoad, this);
33392             return;
33393         }
33394         
33395         this.resize();
33396     },
33397     
33398     onImageLoad : function()
33399     {
33400         this.resize();
33401     },
33402     
33403     resize : function()
33404     {
33405         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33406         
33407         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33408         
33409         if(this.bgimage.length){
33410             var image = this.el.select('.roo-brick-image-view', true).first();
33411             
33412             image.setWidth(paragraph.getWidth());
33413             
33414             if(this.square){
33415                 image.setHeight(paragraph.getWidth());
33416             }
33417             
33418             this.el.setHeight(image.getHeight());
33419             paragraph.setHeight(image.getHeight());
33420             
33421         }
33422         
33423     },
33424     
33425     enter: function(e, el)
33426     {
33427         e.preventDefault();
33428         
33429         if(this.bgimage.length){
33430             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33431             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33432         }
33433     },
33434     
33435     leave: function(e, el)
33436     {
33437         e.preventDefault();
33438         
33439         if(this.bgimage.length){
33440             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33441             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33442         }
33443     }
33444     
33445 });
33446
33447  
33448
33449  /*
33450  * - LGPL
33451  *
33452  * Number field 
33453  */
33454
33455 /**
33456  * @class Roo.bootstrap.NumberField
33457  * @extends Roo.bootstrap.Input
33458  * Bootstrap NumberField class
33459  * 
33460  * 
33461  * 
33462  * 
33463  * @constructor
33464  * Create a new NumberField
33465  * @param {Object} config The config object
33466  */
33467
33468 Roo.bootstrap.NumberField = function(config){
33469     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33470 };
33471
33472 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33473     
33474     /**
33475      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33476      */
33477     allowDecimals : true,
33478     /**
33479      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33480      */
33481     decimalSeparator : ".",
33482     /**
33483      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33484      */
33485     decimalPrecision : 2,
33486     /**
33487      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33488      */
33489     allowNegative : true,
33490     
33491     /**
33492      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33493      */
33494     allowZero: true,
33495     /**
33496      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33497      */
33498     minValue : Number.NEGATIVE_INFINITY,
33499     /**
33500      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33501      */
33502     maxValue : Number.MAX_VALUE,
33503     /**
33504      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33505      */
33506     minText : "The minimum value for this field is {0}",
33507     /**
33508      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33509      */
33510     maxText : "The maximum value for this field is {0}",
33511     /**
33512      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33513      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33514      */
33515     nanText : "{0} is not a valid number",
33516     /**
33517      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33518      */
33519     thousandsDelimiter : false,
33520     /**
33521      * @cfg {String} valueAlign alignment of value
33522      */
33523     valueAlign : "left",
33524
33525     getAutoCreate : function()
33526     {
33527         var hiddenInput = {
33528             tag: 'input',
33529             type: 'hidden',
33530             id: Roo.id(),
33531             cls: 'hidden-number-input'
33532         };
33533         
33534         if (this.name) {
33535             hiddenInput.name = this.name;
33536         }
33537         
33538         this.name = '';
33539         
33540         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33541         
33542         this.name = hiddenInput.name;
33543         
33544         if(cfg.cn.length > 0) {
33545             cfg.cn.push(hiddenInput);
33546         }
33547         
33548         return cfg;
33549     },
33550
33551     // private
33552     initEvents : function()
33553     {   
33554         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33555         
33556         var allowed = "0123456789";
33557         
33558         if(this.allowDecimals){
33559             allowed += this.decimalSeparator;
33560         }
33561         
33562         if(this.allowNegative){
33563             allowed += "-";
33564         }
33565         
33566         if(this.thousandsDelimiter) {
33567             allowed += ",";
33568         }
33569         
33570         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33571         
33572         var keyPress = function(e){
33573             
33574             var k = e.getKey();
33575             
33576             var c = e.getCharCode();
33577             
33578             if(
33579                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33580                     allowed.indexOf(String.fromCharCode(c)) === -1
33581             ){
33582                 e.stopEvent();
33583                 return;
33584             }
33585             
33586             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33587                 return;
33588             }
33589             
33590             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33591                 e.stopEvent();
33592             }
33593         };
33594         
33595         this.el.on("keypress", keyPress, this);
33596     },
33597     
33598     validateValue : function(value)
33599     {
33600         
33601         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33602             return false;
33603         }
33604         
33605         var num = this.parseValue(value);
33606         
33607         if(isNaN(num)){
33608             this.markInvalid(String.format(this.nanText, value));
33609             return false;
33610         }
33611         
33612         if(num < this.minValue){
33613             this.markInvalid(String.format(this.minText, this.minValue));
33614             return false;
33615         }
33616         
33617         if(num > this.maxValue){
33618             this.markInvalid(String.format(this.maxText, this.maxValue));
33619             return false;
33620         }
33621         
33622         return true;
33623     },
33624
33625     getValue : function()
33626     {
33627         var v = this.hiddenEl().getValue();
33628         
33629         return this.fixPrecision(this.parseValue(v));
33630     },
33631
33632     parseValue : function(value)
33633     {
33634         if(this.thousandsDelimiter) {
33635             value += "";
33636             r = new RegExp(",", "g");
33637             value = value.replace(r, "");
33638         }
33639         
33640         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33641         return isNaN(value) ? '' : value;
33642     },
33643
33644     fixPrecision : function(value)
33645     {
33646         if(this.thousandsDelimiter) {
33647             value += "";
33648             r = new RegExp(",", "g");
33649             value = value.replace(r, "");
33650         }
33651         
33652         var nan = isNaN(value);
33653         
33654         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33655             return nan ? '' : value;
33656         }
33657         return parseFloat(value).toFixed(this.decimalPrecision);
33658     },
33659
33660     setValue : function(v)
33661     {
33662         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33663         
33664         this.value = v;
33665         
33666         if(this.rendered){
33667             
33668             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33669             
33670             this.inputEl().dom.value = (v == '') ? '' :
33671                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33672             
33673             if(!this.allowZero && v === '0') {
33674                 this.hiddenEl().dom.value = '';
33675                 this.inputEl().dom.value = '';
33676             }
33677             
33678             this.validate();
33679         }
33680     },
33681
33682     decimalPrecisionFcn : function(v)
33683     {
33684         return Math.floor(v);
33685     },
33686
33687     beforeBlur : function()
33688     {
33689         var v = this.parseValue(this.getRawValue());
33690         
33691         if(v || v === 0 || v === ''){
33692             this.setValue(v);
33693         }
33694     },
33695     
33696     hiddenEl : function()
33697     {
33698         return this.el.select('input.hidden-number-input',true).first();
33699     }
33700     
33701 });
33702
33703  
33704
33705 /*
33706 * Licence: LGPL
33707 */
33708
33709 /**
33710  * @class Roo.bootstrap.DocumentSlider
33711  * @extends Roo.bootstrap.Component
33712  * Bootstrap DocumentSlider class
33713  * 
33714  * @constructor
33715  * Create a new DocumentViewer
33716  * @param {Object} config The config object
33717  */
33718
33719 Roo.bootstrap.DocumentSlider = function(config){
33720     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33721     
33722     this.files = [];
33723     
33724     this.addEvents({
33725         /**
33726          * @event initial
33727          * Fire after initEvent
33728          * @param {Roo.bootstrap.DocumentSlider} this
33729          */
33730         "initial" : true,
33731         /**
33732          * @event update
33733          * Fire after update
33734          * @param {Roo.bootstrap.DocumentSlider} this
33735          */
33736         "update" : true,
33737         /**
33738          * @event click
33739          * Fire after click
33740          * @param {Roo.bootstrap.DocumentSlider} this
33741          */
33742         "click" : true
33743     });
33744 };
33745
33746 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33747     
33748     files : false,
33749     
33750     indicator : 0,
33751     
33752     getAutoCreate : function()
33753     {
33754         var cfg = {
33755             tag : 'div',
33756             cls : 'roo-document-slider',
33757             cn : [
33758                 {
33759                     tag : 'div',
33760                     cls : 'roo-document-slider-header',
33761                     cn : [
33762                         {
33763                             tag : 'div',
33764                             cls : 'roo-document-slider-header-title'
33765                         }
33766                     ]
33767                 },
33768                 {
33769                     tag : 'div',
33770                     cls : 'roo-document-slider-body',
33771                     cn : [
33772                         {
33773                             tag : 'div',
33774                             cls : 'roo-document-slider-prev',
33775                             cn : [
33776                                 {
33777                                     tag : 'i',
33778                                     cls : 'fa fa-chevron-left'
33779                                 }
33780                             ]
33781                         },
33782                         {
33783                             tag : 'div',
33784                             cls : 'roo-document-slider-thumb',
33785                             cn : [
33786                                 {
33787                                     tag : 'img',
33788                                     cls : 'roo-document-slider-image'
33789                                 }
33790                             ]
33791                         },
33792                         {
33793                             tag : 'div',
33794                             cls : 'roo-document-slider-next',
33795                             cn : [
33796                                 {
33797                                     tag : 'i',
33798                                     cls : 'fa fa-chevron-right'
33799                                 }
33800                             ]
33801                         }
33802                     ]
33803                 }
33804             ]
33805         };
33806         
33807         return cfg;
33808     },
33809     
33810     initEvents : function()
33811     {
33812         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33813         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33814         
33815         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33816         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33817         
33818         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33819         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33820         
33821         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33822         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33823         
33824         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33825         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33826         
33827         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33828         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33829         
33830         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33831         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33832         
33833         this.thumbEl.on('click', this.onClick, this);
33834         
33835         this.prevIndicator.on('click', this.prev, this);
33836         
33837         this.nextIndicator.on('click', this.next, this);
33838         
33839     },
33840     
33841     initial : function()
33842     {
33843         if(this.files.length){
33844             this.indicator = 1;
33845             this.update()
33846         }
33847         
33848         this.fireEvent('initial', this);
33849     },
33850     
33851     update : function()
33852     {
33853         this.imageEl.attr('src', this.files[this.indicator - 1]);
33854         
33855         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33856         
33857         this.prevIndicator.show();
33858         
33859         if(this.indicator == 1){
33860             this.prevIndicator.hide();
33861         }
33862         
33863         this.nextIndicator.show();
33864         
33865         if(this.indicator == this.files.length){
33866             this.nextIndicator.hide();
33867         }
33868         
33869         this.thumbEl.scrollTo('top');
33870         
33871         this.fireEvent('update', this);
33872     },
33873     
33874     onClick : function(e)
33875     {
33876         e.preventDefault();
33877         
33878         this.fireEvent('click', this);
33879     },
33880     
33881     prev : function(e)
33882     {
33883         e.preventDefault();
33884         
33885         this.indicator = Math.max(1, this.indicator - 1);
33886         
33887         this.update();
33888     },
33889     
33890     next : function(e)
33891     {
33892         e.preventDefault();
33893         
33894         this.indicator = Math.min(this.files.length, this.indicator + 1);
33895         
33896         this.update();
33897     }
33898 });
33899 /*
33900  * - LGPL
33901  *
33902  * RadioSet
33903  *
33904  *
33905  */
33906
33907 /**
33908  * @class Roo.bootstrap.RadioSet
33909  * @extends Roo.bootstrap.Input
33910  * Bootstrap RadioSet class
33911  * @cfg {String} indicatorpos (left|right) default left
33912  * @cfg {Boolean} inline (true|false) inline the element (default true)
33913  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33914  * @constructor
33915  * Create a new RadioSet
33916  * @param {Object} config The config object
33917  */
33918
33919 Roo.bootstrap.RadioSet = function(config){
33920     
33921     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33922     
33923     this.radioes = [];
33924     
33925     Roo.bootstrap.RadioSet.register(this);
33926     
33927     this.addEvents({
33928         /**
33929         * @event check
33930         * Fires when the element is checked or unchecked.
33931         * @param {Roo.bootstrap.RadioSet} this This radio
33932         * @param {Roo.bootstrap.Radio} item The checked item
33933         */
33934        check : true,
33935        /**
33936         * @event click
33937         * Fires when the element is click.
33938         * @param {Roo.bootstrap.RadioSet} this This radio set
33939         * @param {Roo.bootstrap.Radio} item The checked item
33940         * @param {Roo.EventObject} e The event object
33941         */
33942        click : true
33943     });
33944     
33945 };
33946
33947 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33948
33949     radioes : false,
33950     
33951     inline : true,
33952     
33953     weight : '',
33954     
33955     indicatorpos : 'left',
33956     
33957     getAutoCreate : function()
33958     {
33959         var label = {
33960             tag : 'label',
33961             cls : 'roo-radio-set-label',
33962             cn : [
33963                 {
33964                     tag : 'span',
33965                     html : this.fieldLabel
33966                 }
33967             ]
33968         };
33969         
33970         if(this.indicatorpos == 'left'){
33971             label.cn.unshift({
33972                 tag : 'i',
33973                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33974                 tooltip : 'This field is required'
33975             });
33976         } else {
33977             label.cn.push({
33978                 tag : 'i',
33979                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33980                 tooltip : 'This field is required'
33981             });
33982         }
33983         
33984         var items = {
33985             tag : 'div',
33986             cls : 'roo-radio-set-items'
33987         };
33988         
33989         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33990         
33991         if (align === 'left' && this.fieldLabel.length) {
33992             
33993             items = {
33994                 cls : "roo-radio-set-right", 
33995                 cn: [
33996                     items
33997                 ]
33998             };
33999             
34000             if(this.labelWidth > 12){
34001                 label.style = "width: " + this.labelWidth + 'px';
34002             }
34003             
34004             if(this.labelWidth < 13 && this.labelmd == 0){
34005                 this.labelmd = this.labelWidth;
34006             }
34007             
34008             if(this.labellg > 0){
34009                 label.cls += ' col-lg-' + this.labellg;
34010                 items.cls += ' col-lg-' + (12 - this.labellg);
34011             }
34012             
34013             if(this.labelmd > 0){
34014                 label.cls += ' col-md-' + this.labelmd;
34015                 items.cls += ' col-md-' + (12 - this.labelmd);
34016             }
34017             
34018             if(this.labelsm > 0){
34019                 label.cls += ' col-sm-' + this.labelsm;
34020                 items.cls += ' col-sm-' + (12 - this.labelsm);
34021             }
34022             
34023             if(this.labelxs > 0){
34024                 label.cls += ' col-xs-' + this.labelxs;
34025                 items.cls += ' col-xs-' + (12 - this.labelxs);
34026             }
34027         }
34028         
34029         var cfg = {
34030             tag : 'div',
34031             cls : 'roo-radio-set',
34032             cn : [
34033                 {
34034                     tag : 'input',
34035                     cls : 'roo-radio-set-input',
34036                     type : 'hidden',
34037                     name : this.name,
34038                     value : this.value ? this.value :  ''
34039                 },
34040                 label,
34041                 items
34042             ]
34043         };
34044         
34045         if(this.weight.length){
34046             cfg.cls += ' roo-radio-' + this.weight;
34047         }
34048         
34049         if(this.inline) {
34050             cfg.cls += ' roo-radio-set-inline';
34051         }
34052         
34053         var settings=this;
34054         ['xs','sm','md','lg'].map(function(size){
34055             if (settings[size]) {
34056                 cfg.cls += ' col-' + size + '-' + settings[size];
34057             }
34058         });
34059         
34060         return cfg;
34061         
34062     },
34063
34064     initEvents : function()
34065     {
34066         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34067         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34068         
34069         if(!this.fieldLabel.length){
34070             this.labelEl.hide();
34071         }
34072         
34073         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34074         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34075         
34076         this.indicator = this.indicatorEl();
34077         
34078         if(this.indicator){
34079             this.indicator.addClass('invisible');
34080         }
34081         
34082         this.originalValue = this.getValue();
34083         
34084     },
34085     
34086     inputEl: function ()
34087     {
34088         return this.el.select('.roo-radio-set-input', true).first();
34089     },
34090     
34091     getChildContainer : function()
34092     {
34093         return this.itemsEl;
34094     },
34095     
34096     register : function(item)
34097     {
34098         this.radioes.push(item);
34099         
34100     },
34101     
34102     validate : function()
34103     {   
34104         if(this.getVisibilityEl().hasClass('hidden')){
34105             return true;
34106         }
34107         
34108         var valid = false;
34109         
34110         Roo.each(this.radioes, function(i){
34111             if(!i.checked){
34112                 return;
34113             }
34114             
34115             valid = true;
34116             return false;
34117         });
34118         
34119         if(this.allowBlank) {
34120             return true;
34121         }
34122         
34123         if(this.disabled || valid){
34124             this.markValid();
34125             return true;
34126         }
34127         
34128         this.markInvalid();
34129         return false;
34130         
34131     },
34132     
34133     markValid : function()
34134     {
34135         if(this.labelEl.isVisible(true)){
34136             this.indicatorEl().removeClass('visible');
34137             this.indicatorEl().addClass('invisible');
34138         }
34139         
34140         this.el.removeClass([this.invalidClass, this.validClass]);
34141         this.el.addClass(this.validClass);
34142         
34143         this.fireEvent('valid', this);
34144     },
34145     
34146     markInvalid : function(msg)
34147     {
34148         if(this.allowBlank || this.disabled){
34149             return;
34150         }
34151         
34152         if(this.labelEl.isVisible(true)){
34153             this.indicatorEl().removeClass('invisible');
34154             this.indicatorEl().addClass('visible');
34155         }
34156         
34157         this.el.removeClass([this.invalidClass, this.validClass]);
34158         this.el.addClass(this.invalidClass);
34159         
34160         this.fireEvent('invalid', this, msg);
34161         
34162     },
34163     
34164     setValue : function(v, suppressEvent)
34165     {   
34166         if(this.value === v){
34167             return;
34168         }
34169         
34170         this.value = v;
34171         
34172         if(this.rendered){
34173             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34174         }
34175         
34176         Roo.each(this.radioes, function(i){
34177             i.checked = false;
34178             i.el.removeClass('checked');
34179         });
34180         
34181         Roo.each(this.radioes, function(i){
34182             
34183             if(i.value === v || i.value.toString() === v.toString()){
34184                 i.checked = true;
34185                 i.el.addClass('checked');
34186                 
34187                 if(suppressEvent !== true){
34188                     this.fireEvent('check', this, i);
34189                 }
34190                 
34191                 return false;
34192             }
34193             
34194         }, this);
34195         
34196         this.validate();
34197     },
34198     
34199     clearInvalid : function(){
34200         
34201         if(!this.el || this.preventMark){
34202             return;
34203         }
34204         
34205         this.el.removeClass([this.invalidClass]);
34206         
34207         this.fireEvent('valid', this);
34208     }
34209     
34210 });
34211
34212 Roo.apply(Roo.bootstrap.RadioSet, {
34213     
34214     groups: {},
34215     
34216     register : function(set)
34217     {
34218         this.groups[set.name] = set;
34219     },
34220     
34221     get: function(name) 
34222     {
34223         if (typeof(this.groups[name]) == 'undefined') {
34224             return false;
34225         }
34226         
34227         return this.groups[name] ;
34228     }
34229     
34230 });
34231 /*
34232  * Based on:
34233  * Ext JS Library 1.1.1
34234  * Copyright(c) 2006-2007, Ext JS, LLC.
34235  *
34236  * Originally Released Under LGPL - original licence link has changed is not relivant.
34237  *
34238  * Fork - LGPL
34239  * <script type="text/javascript">
34240  */
34241
34242
34243 /**
34244  * @class Roo.bootstrap.SplitBar
34245  * @extends Roo.util.Observable
34246  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34247  * <br><br>
34248  * Usage:
34249  * <pre><code>
34250 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34251                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34252 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34253 split.minSize = 100;
34254 split.maxSize = 600;
34255 split.animate = true;
34256 split.on('moved', splitterMoved);
34257 </code></pre>
34258  * @constructor
34259  * Create a new SplitBar
34260  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34261  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34262  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34263  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34264                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34265                         position of the SplitBar).
34266  */
34267 Roo.bootstrap.SplitBar = function(cfg){
34268     
34269     /** @private */
34270     
34271     //{
34272     //  dragElement : elm
34273     //  resizingElement: el,
34274         // optional..
34275     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34276     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34277         // existingProxy ???
34278     //}
34279     
34280     this.el = Roo.get(cfg.dragElement, true);
34281     this.el.dom.unselectable = "on";
34282     /** @private */
34283     this.resizingEl = Roo.get(cfg.resizingElement, true);
34284
34285     /**
34286      * @private
34287      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34288      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34289      * @type Number
34290      */
34291     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34292     
34293     /**
34294      * The minimum size of the resizing element. (Defaults to 0)
34295      * @type Number
34296      */
34297     this.minSize = 0;
34298     
34299     /**
34300      * The maximum size of the resizing element. (Defaults to 2000)
34301      * @type Number
34302      */
34303     this.maxSize = 2000;
34304     
34305     /**
34306      * Whether to animate the transition to the new size
34307      * @type Boolean
34308      */
34309     this.animate = false;
34310     
34311     /**
34312      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34313      * @type Boolean
34314      */
34315     this.useShim = false;
34316     
34317     /** @private */
34318     this.shim = null;
34319     
34320     if(!cfg.existingProxy){
34321         /** @private */
34322         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34323     }else{
34324         this.proxy = Roo.get(cfg.existingProxy).dom;
34325     }
34326     /** @private */
34327     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34328     
34329     /** @private */
34330     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34331     
34332     /** @private */
34333     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34334     
34335     /** @private */
34336     this.dragSpecs = {};
34337     
34338     /**
34339      * @private The adapter to use to positon and resize elements
34340      */
34341     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34342     this.adapter.init(this);
34343     
34344     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34345         /** @private */
34346         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34347         this.el.addClass("roo-splitbar-h");
34348     }else{
34349         /** @private */
34350         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34351         this.el.addClass("roo-splitbar-v");
34352     }
34353     
34354     this.addEvents({
34355         /**
34356          * @event resize
34357          * Fires when the splitter is moved (alias for {@link #event-moved})
34358          * @param {Roo.bootstrap.SplitBar} this
34359          * @param {Number} newSize the new width or height
34360          */
34361         "resize" : true,
34362         /**
34363          * @event moved
34364          * Fires when the splitter is moved
34365          * @param {Roo.bootstrap.SplitBar} this
34366          * @param {Number} newSize the new width or height
34367          */
34368         "moved" : true,
34369         /**
34370          * @event beforeresize
34371          * Fires before the splitter is dragged
34372          * @param {Roo.bootstrap.SplitBar} this
34373          */
34374         "beforeresize" : true,
34375
34376         "beforeapply" : true
34377     });
34378
34379     Roo.util.Observable.call(this);
34380 };
34381
34382 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34383     onStartProxyDrag : function(x, y){
34384         this.fireEvent("beforeresize", this);
34385         if(!this.overlay){
34386             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34387             o.unselectable();
34388             o.enableDisplayMode("block");
34389             // all splitbars share the same overlay
34390             Roo.bootstrap.SplitBar.prototype.overlay = o;
34391         }
34392         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34393         this.overlay.show();
34394         Roo.get(this.proxy).setDisplayed("block");
34395         var size = this.adapter.getElementSize(this);
34396         this.activeMinSize = this.getMinimumSize();;
34397         this.activeMaxSize = this.getMaximumSize();;
34398         var c1 = size - this.activeMinSize;
34399         var c2 = Math.max(this.activeMaxSize - size, 0);
34400         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34401             this.dd.resetConstraints();
34402             this.dd.setXConstraint(
34403                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34404                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34405             );
34406             this.dd.setYConstraint(0, 0);
34407         }else{
34408             this.dd.resetConstraints();
34409             this.dd.setXConstraint(0, 0);
34410             this.dd.setYConstraint(
34411                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34412                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34413             );
34414          }
34415         this.dragSpecs.startSize = size;
34416         this.dragSpecs.startPoint = [x, y];
34417         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34418     },
34419     
34420     /** 
34421      * @private Called after the drag operation by the DDProxy
34422      */
34423     onEndProxyDrag : function(e){
34424         Roo.get(this.proxy).setDisplayed(false);
34425         var endPoint = Roo.lib.Event.getXY(e);
34426         if(this.overlay){
34427             this.overlay.hide();
34428         }
34429         var newSize;
34430         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34431             newSize = this.dragSpecs.startSize + 
34432                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34433                     endPoint[0] - this.dragSpecs.startPoint[0] :
34434                     this.dragSpecs.startPoint[0] - endPoint[0]
34435                 );
34436         }else{
34437             newSize = this.dragSpecs.startSize + 
34438                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34439                     endPoint[1] - this.dragSpecs.startPoint[1] :
34440                     this.dragSpecs.startPoint[1] - endPoint[1]
34441                 );
34442         }
34443         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34444         if(newSize != this.dragSpecs.startSize){
34445             if(this.fireEvent('beforeapply', this, newSize) !== false){
34446                 this.adapter.setElementSize(this, newSize);
34447                 this.fireEvent("moved", this, newSize);
34448                 this.fireEvent("resize", this, newSize);
34449             }
34450         }
34451     },
34452     
34453     /**
34454      * Get the adapter this SplitBar uses
34455      * @return The adapter object
34456      */
34457     getAdapter : function(){
34458         return this.adapter;
34459     },
34460     
34461     /**
34462      * Set the adapter this SplitBar uses
34463      * @param {Object} adapter A SplitBar adapter object
34464      */
34465     setAdapter : function(adapter){
34466         this.adapter = adapter;
34467         this.adapter.init(this);
34468     },
34469     
34470     /**
34471      * Gets the minimum size for the resizing element
34472      * @return {Number} The minimum size
34473      */
34474     getMinimumSize : function(){
34475         return this.minSize;
34476     },
34477     
34478     /**
34479      * Sets the minimum size for the resizing element
34480      * @param {Number} minSize The minimum size
34481      */
34482     setMinimumSize : function(minSize){
34483         this.minSize = minSize;
34484     },
34485     
34486     /**
34487      * Gets the maximum size for the resizing element
34488      * @return {Number} The maximum size
34489      */
34490     getMaximumSize : function(){
34491         return this.maxSize;
34492     },
34493     
34494     /**
34495      * Sets the maximum size for the resizing element
34496      * @param {Number} maxSize The maximum size
34497      */
34498     setMaximumSize : function(maxSize){
34499         this.maxSize = maxSize;
34500     },
34501     
34502     /**
34503      * Sets the initialize size for the resizing element
34504      * @param {Number} size The initial size
34505      */
34506     setCurrentSize : function(size){
34507         var oldAnimate = this.animate;
34508         this.animate = false;
34509         this.adapter.setElementSize(this, size);
34510         this.animate = oldAnimate;
34511     },
34512     
34513     /**
34514      * Destroy this splitbar. 
34515      * @param {Boolean} removeEl True to remove the element
34516      */
34517     destroy : function(removeEl){
34518         if(this.shim){
34519             this.shim.remove();
34520         }
34521         this.dd.unreg();
34522         this.proxy.parentNode.removeChild(this.proxy);
34523         if(removeEl){
34524             this.el.remove();
34525         }
34526     }
34527 });
34528
34529 /**
34530  * @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.
34531  */
34532 Roo.bootstrap.SplitBar.createProxy = function(dir){
34533     var proxy = new Roo.Element(document.createElement("div"));
34534     proxy.unselectable();
34535     var cls = 'roo-splitbar-proxy';
34536     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34537     document.body.appendChild(proxy.dom);
34538     return proxy.dom;
34539 };
34540
34541 /** 
34542  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34543  * Default Adapter. It assumes the splitter and resizing element are not positioned
34544  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34545  */
34546 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34547 };
34548
34549 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34550     // do nothing for now
34551     init : function(s){
34552     
34553     },
34554     /**
34555      * Called before drag operations to get the current size of the resizing element. 
34556      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34557      */
34558      getElementSize : function(s){
34559         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34560             return s.resizingEl.getWidth();
34561         }else{
34562             return s.resizingEl.getHeight();
34563         }
34564     },
34565     
34566     /**
34567      * Called after drag operations to set the size of the resizing element.
34568      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34569      * @param {Number} newSize The new size to set
34570      * @param {Function} onComplete A function to be invoked when resizing is complete
34571      */
34572     setElementSize : function(s, newSize, onComplete){
34573         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34574             if(!s.animate){
34575                 s.resizingEl.setWidth(newSize);
34576                 if(onComplete){
34577                     onComplete(s, newSize);
34578                 }
34579             }else{
34580                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34581             }
34582         }else{
34583             
34584             if(!s.animate){
34585                 s.resizingEl.setHeight(newSize);
34586                 if(onComplete){
34587                     onComplete(s, newSize);
34588                 }
34589             }else{
34590                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34591             }
34592         }
34593     }
34594 };
34595
34596 /** 
34597  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34598  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34599  * Adapter that  moves the splitter element to align with the resized sizing element. 
34600  * Used with an absolute positioned SplitBar.
34601  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34602  * document.body, make sure you assign an id to the body element.
34603  */
34604 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34605     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34606     this.container = Roo.get(container);
34607 };
34608
34609 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34610     init : function(s){
34611         this.basic.init(s);
34612     },
34613     
34614     getElementSize : function(s){
34615         return this.basic.getElementSize(s);
34616     },
34617     
34618     setElementSize : function(s, newSize, onComplete){
34619         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34620     },
34621     
34622     moveSplitter : function(s){
34623         var yes = Roo.bootstrap.SplitBar;
34624         switch(s.placement){
34625             case yes.LEFT:
34626                 s.el.setX(s.resizingEl.getRight());
34627                 break;
34628             case yes.RIGHT:
34629                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34630                 break;
34631             case yes.TOP:
34632                 s.el.setY(s.resizingEl.getBottom());
34633                 break;
34634             case yes.BOTTOM:
34635                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34636                 break;
34637         }
34638     }
34639 };
34640
34641 /**
34642  * Orientation constant - Create a vertical SplitBar
34643  * @static
34644  * @type Number
34645  */
34646 Roo.bootstrap.SplitBar.VERTICAL = 1;
34647
34648 /**
34649  * Orientation constant - Create a horizontal SplitBar
34650  * @static
34651  * @type Number
34652  */
34653 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34654
34655 /**
34656  * Placement constant - The resizing element is to the left of the splitter element
34657  * @static
34658  * @type Number
34659  */
34660 Roo.bootstrap.SplitBar.LEFT = 1;
34661
34662 /**
34663  * Placement constant - The resizing element is to the right of the splitter element
34664  * @static
34665  * @type Number
34666  */
34667 Roo.bootstrap.SplitBar.RIGHT = 2;
34668
34669 /**
34670  * Placement constant - The resizing element is positioned above the splitter element
34671  * @static
34672  * @type Number
34673  */
34674 Roo.bootstrap.SplitBar.TOP = 3;
34675
34676 /**
34677  * Placement constant - The resizing element is positioned under splitter element
34678  * @static
34679  * @type Number
34680  */
34681 Roo.bootstrap.SplitBar.BOTTOM = 4;
34682 Roo.namespace("Roo.bootstrap.layout");/*
34683  * Based on:
34684  * Ext JS Library 1.1.1
34685  * Copyright(c) 2006-2007, Ext JS, LLC.
34686  *
34687  * Originally Released Under LGPL - original licence link has changed is not relivant.
34688  *
34689  * Fork - LGPL
34690  * <script type="text/javascript">
34691  */
34692
34693 /**
34694  * @class Roo.bootstrap.layout.Manager
34695  * @extends Roo.bootstrap.Component
34696  * Base class for layout managers.
34697  */
34698 Roo.bootstrap.layout.Manager = function(config)
34699 {
34700     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34701
34702
34703
34704
34705
34706     /** false to disable window resize monitoring @type Boolean */
34707     this.monitorWindowResize = true;
34708     this.regions = {};
34709     this.addEvents({
34710         /**
34711          * @event layout
34712          * Fires when a layout is performed.
34713          * @param {Roo.LayoutManager} this
34714          */
34715         "layout" : true,
34716         /**
34717          * @event regionresized
34718          * Fires when the user resizes a region.
34719          * @param {Roo.LayoutRegion} region The resized region
34720          * @param {Number} newSize The new size (width for east/west, height for north/south)
34721          */
34722         "regionresized" : true,
34723         /**
34724          * @event regioncollapsed
34725          * Fires when a region is collapsed.
34726          * @param {Roo.LayoutRegion} region The collapsed region
34727          */
34728         "regioncollapsed" : true,
34729         /**
34730          * @event regionexpanded
34731          * Fires when a region is expanded.
34732          * @param {Roo.LayoutRegion} region The expanded region
34733          */
34734         "regionexpanded" : true
34735     });
34736     this.updating = false;
34737
34738     if (config.el) {
34739         this.el = Roo.get(config.el);
34740         this.initEvents();
34741     }
34742
34743 };
34744
34745 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34746
34747
34748     regions : null,
34749
34750     monitorWindowResize : true,
34751
34752
34753     updating : false,
34754
34755
34756     onRender : function(ct, position)
34757     {
34758         if(!this.el){
34759             this.el = Roo.get(ct);
34760             this.initEvents();
34761         }
34762         //this.fireEvent('render',this);
34763     },
34764
34765
34766     initEvents: function()
34767     {
34768
34769
34770         // ie scrollbar fix
34771         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34772             document.body.scroll = "no";
34773         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34774             this.el.position('relative');
34775         }
34776         this.id = this.el.id;
34777         this.el.addClass("roo-layout-container");
34778         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34779         if(this.el.dom != document.body ) {
34780             this.el.on('resize', this.layout,this);
34781             this.el.on('show', this.layout,this);
34782         }
34783
34784     },
34785
34786     /**
34787      * Returns true if this layout is currently being updated
34788      * @return {Boolean}
34789      */
34790     isUpdating : function(){
34791         return this.updating;
34792     },
34793
34794     /**
34795      * Suspend the LayoutManager from doing auto-layouts while
34796      * making multiple add or remove calls
34797      */
34798     beginUpdate : function(){
34799         this.updating = true;
34800     },
34801
34802     /**
34803      * Restore auto-layouts and optionally disable the manager from performing a layout
34804      * @param {Boolean} noLayout true to disable a layout update
34805      */
34806     endUpdate : function(noLayout){
34807         this.updating = false;
34808         if(!noLayout){
34809             this.layout();
34810         }
34811     },
34812
34813     layout: function(){
34814         // abstract...
34815     },
34816
34817     onRegionResized : function(region, newSize){
34818         this.fireEvent("regionresized", region, newSize);
34819         this.layout();
34820     },
34821
34822     onRegionCollapsed : function(region){
34823         this.fireEvent("regioncollapsed", region);
34824     },
34825
34826     onRegionExpanded : function(region){
34827         this.fireEvent("regionexpanded", region);
34828     },
34829
34830     /**
34831      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34832      * performs box-model adjustments.
34833      * @return {Object} The size as an object {width: (the width), height: (the height)}
34834      */
34835     getViewSize : function()
34836     {
34837         var size;
34838         if(this.el.dom != document.body){
34839             size = this.el.getSize();
34840         }else{
34841             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34842         }
34843         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34844         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34845         return size;
34846     },
34847
34848     /**
34849      * Returns the Element this layout is bound to.
34850      * @return {Roo.Element}
34851      */
34852     getEl : function(){
34853         return this.el;
34854     },
34855
34856     /**
34857      * Returns the specified region.
34858      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34859      * @return {Roo.LayoutRegion}
34860      */
34861     getRegion : function(target){
34862         return this.regions[target.toLowerCase()];
34863     },
34864
34865     onWindowResize : function(){
34866         if(this.monitorWindowResize){
34867             this.layout();
34868         }
34869     }
34870 });
34871 /*
34872  * Based on:
34873  * Ext JS Library 1.1.1
34874  * Copyright(c) 2006-2007, Ext JS, LLC.
34875  *
34876  * Originally Released Under LGPL - original licence link has changed is not relivant.
34877  *
34878  * Fork - LGPL
34879  * <script type="text/javascript">
34880  */
34881 /**
34882  * @class Roo.bootstrap.layout.Border
34883  * @extends Roo.bootstrap.layout.Manager
34884  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34885  * please see: examples/bootstrap/nested.html<br><br>
34886  
34887 <b>The container the layout is rendered into can be either the body element or any other element.
34888 If it is not the body element, the container needs to either be an absolute positioned element,
34889 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34890 the container size if it is not the body element.</b>
34891
34892 * @constructor
34893 * Create a new Border
34894 * @param {Object} config Configuration options
34895  */
34896 Roo.bootstrap.layout.Border = function(config){
34897     config = config || {};
34898     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34899     
34900     
34901     
34902     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34903         if(config[region]){
34904             config[region].region = region;
34905             this.addRegion(config[region]);
34906         }
34907     },this);
34908     
34909 };
34910
34911 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34912
34913 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34914     /**
34915      * Creates and adds a new region if it doesn't already exist.
34916      * @param {String} target The target region key (north, south, east, west or center).
34917      * @param {Object} config The regions config object
34918      * @return {BorderLayoutRegion} The new region
34919      */
34920     addRegion : function(config)
34921     {
34922         if(!this.regions[config.region]){
34923             var r = this.factory(config);
34924             this.bindRegion(r);
34925         }
34926         return this.regions[config.region];
34927     },
34928
34929     // private (kinda)
34930     bindRegion : function(r){
34931         this.regions[r.config.region] = r;
34932         
34933         r.on("visibilitychange",    this.layout, this);
34934         r.on("paneladded",          this.layout, this);
34935         r.on("panelremoved",        this.layout, this);
34936         r.on("invalidated",         this.layout, this);
34937         r.on("resized",             this.onRegionResized, this);
34938         r.on("collapsed",           this.onRegionCollapsed, this);
34939         r.on("expanded",            this.onRegionExpanded, this);
34940     },
34941
34942     /**
34943      * Performs a layout update.
34944      */
34945     layout : function()
34946     {
34947         if(this.updating) {
34948             return;
34949         }
34950         
34951         // render all the rebions if they have not been done alreayd?
34952         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34953             if(this.regions[region] && !this.regions[region].bodyEl){
34954                 this.regions[region].onRender(this.el)
34955             }
34956         },this);
34957         
34958         var size = this.getViewSize();
34959         var w = size.width;
34960         var h = size.height;
34961         var centerW = w;
34962         var centerH = h;
34963         var centerY = 0;
34964         var centerX = 0;
34965         //var x = 0, y = 0;
34966
34967         var rs = this.regions;
34968         var north = rs["north"];
34969         var south = rs["south"]; 
34970         var west = rs["west"];
34971         var east = rs["east"];
34972         var center = rs["center"];
34973         //if(this.hideOnLayout){ // not supported anymore
34974             //c.el.setStyle("display", "none");
34975         //}
34976         if(north && north.isVisible()){
34977             var b = north.getBox();
34978             var m = north.getMargins();
34979             b.width = w - (m.left+m.right);
34980             b.x = m.left;
34981             b.y = m.top;
34982             centerY = b.height + b.y + m.bottom;
34983             centerH -= centerY;
34984             north.updateBox(this.safeBox(b));
34985         }
34986         if(south && south.isVisible()){
34987             var b = south.getBox();
34988             var m = south.getMargins();
34989             b.width = w - (m.left+m.right);
34990             b.x = m.left;
34991             var totalHeight = (b.height + m.top + m.bottom);
34992             b.y = h - totalHeight + m.top;
34993             centerH -= totalHeight;
34994             south.updateBox(this.safeBox(b));
34995         }
34996         if(west && west.isVisible()){
34997             var b = west.getBox();
34998             var m = west.getMargins();
34999             b.height = centerH - (m.top+m.bottom);
35000             b.x = m.left;
35001             b.y = centerY + m.top;
35002             var totalWidth = (b.width + m.left + m.right);
35003             centerX += totalWidth;
35004             centerW -= totalWidth;
35005             west.updateBox(this.safeBox(b));
35006         }
35007         if(east && east.isVisible()){
35008             var b = east.getBox();
35009             var m = east.getMargins();
35010             b.height = centerH - (m.top+m.bottom);
35011             var totalWidth = (b.width + m.left + m.right);
35012             b.x = w - totalWidth + m.left;
35013             b.y = centerY + m.top;
35014             centerW -= totalWidth;
35015             east.updateBox(this.safeBox(b));
35016         }
35017         if(center){
35018             var m = center.getMargins();
35019             var centerBox = {
35020                 x: centerX + m.left,
35021                 y: centerY + m.top,
35022                 width: centerW - (m.left+m.right),
35023                 height: centerH - (m.top+m.bottom)
35024             };
35025             //if(this.hideOnLayout){
35026                 //center.el.setStyle("display", "block");
35027             //}
35028             center.updateBox(this.safeBox(centerBox));
35029         }
35030         this.el.repaint();
35031         this.fireEvent("layout", this);
35032     },
35033
35034     // private
35035     safeBox : function(box){
35036         box.width = Math.max(0, box.width);
35037         box.height = Math.max(0, box.height);
35038         return box;
35039     },
35040
35041     /**
35042      * Adds a ContentPanel (or subclass) to this layout.
35043      * @param {String} target The target region key (north, south, east, west or center).
35044      * @param {Roo.ContentPanel} panel The panel to add
35045      * @return {Roo.ContentPanel} The added panel
35046      */
35047     add : function(target, panel){
35048          
35049         target = target.toLowerCase();
35050         return this.regions[target].add(panel);
35051     },
35052
35053     /**
35054      * Remove a ContentPanel (or subclass) to this layout.
35055      * @param {String} target The target region key (north, south, east, west or center).
35056      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35057      * @return {Roo.ContentPanel} The removed panel
35058      */
35059     remove : function(target, panel){
35060         target = target.toLowerCase();
35061         return this.regions[target].remove(panel);
35062     },
35063
35064     /**
35065      * Searches all regions for a panel with the specified id
35066      * @param {String} panelId
35067      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35068      */
35069     findPanel : function(panelId){
35070         var rs = this.regions;
35071         for(var target in rs){
35072             if(typeof rs[target] != "function"){
35073                 var p = rs[target].getPanel(panelId);
35074                 if(p){
35075                     return p;
35076                 }
35077             }
35078         }
35079         return null;
35080     },
35081
35082     /**
35083      * Searches all regions for a panel with the specified id and activates (shows) it.
35084      * @param {String/ContentPanel} panelId The panels id or the panel itself
35085      * @return {Roo.ContentPanel} The shown panel or null
35086      */
35087     showPanel : function(panelId) {
35088       var rs = this.regions;
35089       for(var target in rs){
35090          var r = rs[target];
35091          if(typeof r != "function"){
35092             if(r.hasPanel(panelId)){
35093                return r.showPanel(panelId);
35094             }
35095          }
35096       }
35097       return null;
35098    },
35099
35100    /**
35101      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35102      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35103      */
35104    /*
35105     restoreState : function(provider){
35106         if(!provider){
35107             provider = Roo.state.Manager;
35108         }
35109         var sm = new Roo.LayoutStateManager();
35110         sm.init(this, provider);
35111     },
35112 */
35113  
35114  
35115     /**
35116      * Adds a xtype elements to the layout.
35117      * <pre><code>
35118
35119 layout.addxtype({
35120        xtype : 'ContentPanel',
35121        region: 'west',
35122        items: [ .... ]
35123    }
35124 );
35125
35126 layout.addxtype({
35127         xtype : 'NestedLayoutPanel',
35128         region: 'west',
35129         layout: {
35130            center: { },
35131            west: { }   
35132         },
35133         items : [ ... list of content panels or nested layout panels.. ]
35134    }
35135 );
35136 </code></pre>
35137      * @param {Object} cfg Xtype definition of item to add.
35138      */
35139     addxtype : function(cfg)
35140     {
35141         // basically accepts a pannel...
35142         // can accept a layout region..!?!?
35143         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35144         
35145         
35146         // theory?  children can only be panels??
35147         
35148         //if (!cfg.xtype.match(/Panel$/)) {
35149         //    return false;
35150         //}
35151         var ret = false;
35152         
35153         if (typeof(cfg.region) == 'undefined') {
35154             Roo.log("Failed to add Panel, region was not set");
35155             Roo.log(cfg);
35156             return false;
35157         }
35158         var region = cfg.region;
35159         delete cfg.region;
35160         
35161           
35162         var xitems = [];
35163         if (cfg.items) {
35164             xitems = cfg.items;
35165             delete cfg.items;
35166         }
35167         var nb = false;
35168         
35169         switch(cfg.xtype) 
35170         {
35171             case 'Content':  // ContentPanel (el, cfg)
35172             case 'Scroll':  // ContentPanel (el, cfg)
35173             case 'View': 
35174                 cfg.autoCreate = true;
35175                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35176                 //} else {
35177                 //    var el = this.el.createChild();
35178                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35179                 //}
35180                 
35181                 this.add(region, ret);
35182                 break;
35183             
35184             /*
35185             case 'TreePanel': // our new panel!
35186                 cfg.el = this.el.createChild();
35187                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35188                 this.add(region, ret);
35189                 break;
35190             */
35191             
35192             case 'Nest': 
35193                 // create a new Layout (which is  a Border Layout...
35194                 
35195                 var clayout = cfg.layout;
35196                 clayout.el  = this.el.createChild();
35197                 clayout.items   = clayout.items  || [];
35198                 
35199                 delete cfg.layout;
35200                 
35201                 // replace this exitems with the clayout ones..
35202                 xitems = clayout.items;
35203                  
35204                 // force background off if it's in center...
35205                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35206                     cfg.background = false;
35207                 }
35208                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35209                 
35210                 
35211                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35212                 //console.log('adding nested layout panel '  + cfg.toSource());
35213                 this.add(region, ret);
35214                 nb = {}; /// find first...
35215                 break;
35216             
35217             case 'Grid':
35218                 
35219                 // needs grid and region
35220                 
35221                 //var el = this.getRegion(region).el.createChild();
35222                 /*
35223                  *var el = this.el.createChild();
35224                 // create the grid first...
35225                 cfg.grid.container = el;
35226                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35227                 */
35228                 
35229                 if (region == 'center' && this.active ) {
35230                     cfg.background = false;
35231                 }
35232                 
35233                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35234                 
35235                 this.add(region, ret);
35236                 /*
35237                 if (cfg.background) {
35238                     // render grid on panel activation (if panel background)
35239                     ret.on('activate', function(gp) {
35240                         if (!gp.grid.rendered) {
35241                     //        gp.grid.render(el);
35242                         }
35243                     });
35244                 } else {
35245                   //  cfg.grid.render(el);
35246                 }
35247                 */
35248                 break;
35249            
35250            
35251             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35252                 // it was the old xcomponent building that caused this before.
35253                 // espeically if border is the top element in the tree.
35254                 ret = this;
35255                 break; 
35256                 
35257                     
35258                 
35259                 
35260                 
35261             default:
35262                 /*
35263                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35264                     
35265                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35266                     this.add(region, ret);
35267                 } else {
35268                 */
35269                     Roo.log(cfg);
35270                     throw "Can not add '" + cfg.xtype + "' to Border";
35271                     return null;
35272              
35273                                 
35274              
35275         }
35276         this.beginUpdate();
35277         // add children..
35278         var region = '';
35279         var abn = {};
35280         Roo.each(xitems, function(i)  {
35281             region = nb && i.region ? i.region : false;
35282             
35283             var add = ret.addxtype(i);
35284            
35285             if (region) {
35286                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35287                 if (!i.background) {
35288                     abn[region] = nb[region] ;
35289                 }
35290             }
35291             
35292         });
35293         this.endUpdate();
35294
35295         // make the last non-background panel active..
35296         //if (nb) { Roo.log(abn); }
35297         if (nb) {
35298             
35299             for(var r in abn) {
35300                 region = this.getRegion(r);
35301                 if (region) {
35302                     // tried using nb[r], but it does not work..
35303                      
35304                     region.showPanel(abn[r]);
35305                    
35306                 }
35307             }
35308         }
35309         return ret;
35310         
35311     },
35312     
35313     
35314 // private
35315     factory : function(cfg)
35316     {
35317         
35318         var validRegions = Roo.bootstrap.layout.Border.regions;
35319
35320         var target = cfg.region;
35321         cfg.mgr = this;
35322         
35323         var r = Roo.bootstrap.layout;
35324         Roo.log(target);
35325         switch(target){
35326             case "north":
35327                 return new r.North(cfg);
35328             case "south":
35329                 return new r.South(cfg);
35330             case "east":
35331                 return new r.East(cfg);
35332             case "west":
35333                 return new r.West(cfg);
35334             case "center":
35335                 return new r.Center(cfg);
35336         }
35337         throw 'Layout region "'+target+'" not supported.';
35338     }
35339     
35340     
35341 });
35342  /*
35343  * Based on:
35344  * Ext JS Library 1.1.1
35345  * Copyright(c) 2006-2007, Ext JS, LLC.
35346  *
35347  * Originally Released Under LGPL - original licence link has changed is not relivant.
35348  *
35349  * Fork - LGPL
35350  * <script type="text/javascript">
35351  */
35352  
35353 /**
35354  * @class Roo.bootstrap.layout.Basic
35355  * @extends Roo.util.Observable
35356  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35357  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35358  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35359  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35360  * @cfg {string}   region  the region that it inhabits..
35361  * @cfg {bool}   skipConfig skip config?
35362  * 
35363
35364  */
35365 Roo.bootstrap.layout.Basic = function(config){
35366     
35367     this.mgr = config.mgr;
35368     
35369     this.position = config.region;
35370     
35371     var skipConfig = config.skipConfig;
35372     
35373     this.events = {
35374         /**
35375          * @scope Roo.BasicLayoutRegion
35376          */
35377         
35378         /**
35379          * @event beforeremove
35380          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35381          * @param {Roo.LayoutRegion} this
35382          * @param {Roo.ContentPanel} panel The panel
35383          * @param {Object} e The cancel event object
35384          */
35385         "beforeremove" : true,
35386         /**
35387          * @event invalidated
35388          * Fires when the layout for this region is changed.
35389          * @param {Roo.LayoutRegion} this
35390          */
35391         "invalidated" : true,
35392         /**
35393          * @event visibilitychange
35394          * Fires when this region is shown or hidden 
35395          * @param {Roo.LayoutRegion} this
35396          * @param {Boolean} visibility true or false
35397          */
35398         "visibilitychange" : true,
35399         /**
35400          * @event paneladded
35401          * Fires when a panel is added. 
35402          * @param {Roo.LayoutRegion} this
35403          * @param {Roo.ContentPanel} panel The panel
35404          */
35405         "paneladded" : true,
35406         /**
35407          * @event panelremoved
35408          * Fires when a panel is removed. 
35409          * @param {Roo.LayoutRegion} this
35410          * @param {Roo.ContentPanel} panel The panel
35411          */
35412         "panelremoved" : true,
35413         /**
35414          * @event beforecollapse
35415          * Fires when this region before collapse.
35416          * @param {Roo.LayoutRegion} this
35417          */
35418         "beforecollapse" : true,
35419         /**
35420          * @event collapsed
35421          * Fires when this region is collapsed.
35422          * @param {Roo.LayoutRegion} this
35423          */
35424         "collapsed" : true,
35425         /**
35426          * @event expanded
35427          * Fires when this region is expanded.
35428          * @param {Roo.LayoutRegion} this
35429          */
35430         "expanded" : true,
35431         /**
35432          * @event slideshow
35433          * Fires when this region is slid into view.
35434          * @param {Roo.LayoutRegion} this
35435          */
35436         "slideshow" : true,
35437         /**
35438          * @event slidehide
35439          * Fires when this region slides out of view. 
35440          * @param {Roo.LayoutRegion} this
35441          */
35442         "slidehide" : true,
35443         /**
35444          * @event panelactivated
35445          * Fires when a panel is activated. 
35446          * @param {Roo.LayoutRegion} this
35447          * @param {Roo.ContentPanel} panel The activated panel
35448          */
35449         "panelactivated" : true,
35450         /**
35451          * @event resized
35452          * Fires when the user resizes this region. 
35453          * @param {Roo.LayoutRegion} this
35454          * @param {Number} newSize The new size (width for east/west, height for north/south)
35455          */
35456         "resized" : true
35457     };
35458     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35459     this.panels = new Roo.util.MixedCollection();
35460     this.panels.getKey = this.getPanelId.createDelegate(this);
35461     this.box = null;
35462     this.activePanel = null;
35463     // ensure listeners are added...
35464     
35465     if (config.listeners || config.events) {
35466         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35467             listeners : config.listeners || {},
35468             events : config.events || {}
35469         });
35470     }
35471     
35472     if(skipConfig !== true){
35473         this.applyConfig(config);
35474     }
35475 };
35476
35477 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35478 {
35479     getPanelId : function(p){
35480         return p.getId();
35481     },
35482     
35483     applyConfig : function(config){
35484         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35485         this.config = config;
35486         
35487     },
35488     
35489     /**
35490      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35491      * the width, for horizontal (north, south) the height.
35492      * @param {Number} newSize The new width or height
35493      */
35494     resizeTo : function(newSize){
35495         var el = this.el ? this.el :
35496                  (this.activePanel ? this.activePanel.getEl() : null);
35497         if(el){
35498             switch(this.position){
35499                 case "east":
35500                 case "west":
35501                     el.setWidth(newSize);
35502                     this.fireEvent("resized", this, newSize);
35503                 break;
35504                 case "north":
35505                 case "south":
35506                     el.setHeight(newSize);
35507                     this.fireEvent("resized", this, newSize);
35508                 break;                
35509             }
35510         }
35511     },
35512     
35513     getBox : function(){
35514         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35515     },
35516     
35517     getMargins : function(){
35518         return this.margins;
35519     },
35520     
35521     updateBox : function(box){
35522         this.box = box;
35523         var el = this.activePanel.getEl();
35524         el.dom.style.left = box.x + "px";
35525         el.dom.style.top = box.y + "px";
35526         this.activePanel.setSize(box.width, box.height);
35527     },
35528     
35529     /**
35530      * Returns the container element for this region.
35531      * @return {Roo.Element}
35532      */
35533     getEl : function(){
35534         return this.activePanel;
35535     },
35536     
35537     /**
35538      * Returns true if this region is currently visible.
35539      * @return {Boolean}
35540      */
35541     isVisible : function(){
35542         return this.activePanel ? true : false;
35543     },
35544     
35545     setActivePanel : function(panel){
35546         panel = this.getPanel(panel);
35547         if(this.activePanel && this.activePanel != panel){
35548             this.activePanel.setActiveState(false);
35549             this.activePanel.getEl().setLeftTop(-10000,-10000);
35550         }
35551         this.activePanel = panel;
35552         panel.setActiveState(true);
35553         if(this.box){
35554             panel.setSize(this.box.width, this.box.height);
35555         }
35556         this.fireEvent("panelactivated", this, panel);
35557         this.fireEvent("invalidated");
35558     },
35559     
35560     /**
35561      * Show the specified panel.
35562      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35563      * @return {Roo.ContentPanel} The shown panel or null
35564      */
35565     showPanel : function(panel){
35566         panel = this.getPanel(panel);
35567         if(panel){
35568             this.setActivePanel(panel);
35569         }
35570         return panel;
35571     },
35572     
35573     /**
35574      * Get the active panel for this region.
35575      * @return {Roo.ContentPanel} The active panel or null
35576      */
35577     getActivePanel : function(){
35578         return this.activePanel;
35579     },
35580     
35581     /**
35582      * Add the passed ContentPanel(s)
35583      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35584      * @return {Roo.ContentPanel} The panel added (if only one was added)
35585      */
35586     add : function(panel){
35587         if(arguments.length > 1){
35588             for(var i = 0, len = arguments.length; i < len; i++) {
35589                 this.add(arguments[i]);
35590             }
35591             return null;
35592         }
35593         if(this.hasPanel(panel)){
35594             this.showPanel(panel);
35595             return panel;
35596         }
35597         var el = panel.getEl();
35598         if(el.dom.parentNode != this.mgr.el.dom){
35599             this.mgr.el.dom.appendChild(el.dom);
35600         }
35601         if(panel.setRegion){
35602             panel.setRegion(this);
35603         }
35604         this.panels.add(panel);
35605         el.setStyle("position", "absolute");
35606         if(!panel.background){
35607             this.setActivePanel(panel);
35608             if(this.config.initialSize && this.panels.getCount()==1){
35609                 this.resizeTo(this.config.initialSize);
35610             }
35611         }
35612         this.fireEvent("paneladded", this, panel);
35613         return panel;
35614     },
35615     
35616     /**
35617      * Returns true if the panel is in this region.
35618      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35619      * @return {Boolean}
35620      */
35621     hasPanel : function(panel){
35622         if(typeof panel == "object"){ // must be panel obj
35623             panel = panel.getId();
35624         }
35625         return this.getPanel(panel) ? true : false;
35626     },
35627     
35628     /**
35629      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35630      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35631      * @param {Boolean} preservePanel Overrides the config preservePanel option
35632      * @return {Roo.ContentPanel} The panel that was removed
35633      */
35634     remove : function(panel, preservePanel){
35635         panel = this.getPanel(panel);
35636         if(!panel){
35637             return null;
35638         }
35639         var e = {};
35640         this.fireEvent("beforeremove", this, panel, e);
35641         if(e.cancel === true){
35642             return null;
35643         }
35644         var panelId = panel.getId();
35645         this.panels.removeKey(panelId);
35646         return panel;
35647     },
35648     
35649     /**
35650      * Returns the panel specified or null if it's not in this region.
35651      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35652      * @return {Roo.ContentPanel}
35653      */
35654     getPanel : function(id){
35655         if(typeof id == "object"){ // must be panel obj
35656             return id;
35657         }
35658         return this.panels.get(id);
35659     },
35660     
35661     /**
35662      * Returns this regions position (north/south/east/west/center).
35663      * @return {String} 
35664      */
35665     getPosition: function(){
35666         return this.position;    
35667     }
35668 });/*
35669  * Based on:
35670  * Ext JS Library 1.1.1
35671  * Copyright(c) 2006-2007, Ext JS, LLC.
35672  *
35673  * Originally Released Under LGPL - original licence link has changed is not relivant.
35674  *
35675  * Fork - LGPL
35676  * <script type="text/javascript">
35677  */
35678  
35679 /**
35680  * @class Roo.bootstrap.layout.Region
35681  * @extends Roo.bootstrap.layout.Basic
35682  * This class represents a region in a layout manager.
35683  
35684  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35685  * @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})
35686  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35687  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35688  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35689  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35690  * @cfg {String}    title           The title for the region (overrides panel titles)
35691  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35692  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35693  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35694  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35695  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35696  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35697  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35698  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35699  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35700  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35701
35702  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35703  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35704  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35705  * @cfg {Number}    width           For East/West panels
35706  * @cfg {Number}    height          For North/South panels
35707  * @cfg {Boolean}   split           To show the splitter
35708  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35709  * 
35710  * @cfg {string}   cls             Extra CSS classes to add to region
35711  * 
35712  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35713  * @cfg {string}   region  the region that it inhabits..
35714  *
35715
35716  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35717  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35718
35719  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35720  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35721  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35722  */
35723 Roo.bootstrap.layout.Region = function(config)
35724 {
35725     this.applyConfig(config);
35726
35727     var mgr = config.mgr;
35728     var pos = config.region;
35729     config.skipConfig = true;
35730     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35731     
35732     if (mgr.el) {
35733         this.onRender(mgr.el);   
35734     }
35735      
35736     this.visible = true;
35737     this.collapsed = false;
35738     this.unrendered_panels = [];
35739 };
35740
35741 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35742
35743     position: '', // set by wrapper (eg. north/south etc..)
35744     unrendered_panels : null,  // unrendered panels.
35745     createBody : function(){
35746         /** This region's body element 
35747         * @type Roo.Element */
35748         this.bodyEl = this.el.createChild({
35749                 tag: "div",
35750                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35751         });
35752     },
35753
35754     onRender: function(ctr, pos)
35755     {
35756         var dh = Roo.DomHelper;
35757         /** This region's container element 
35758         * @type Roo.Element */
35759         this.el = dh.append(ctr.dom, {
35760                 tag: "div",
35761                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35762             }, true);
35763         /** This region's title element 
35764         * @type Roo.Element */
35765     
35766         this.titleEl = dh.append(this.el.dom,
35767             {
35768                     tag: "div",
35769                     unselectable: "on",
35770                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35771                     children:[
35772                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35773                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35774                     ]}, true);
35775         
35776         this.titleEl.enableDisplayMode();
35777         /** This region's title text element 
35778         * @type HTMLElement */
35779         this.titleTextEl = this.titleEl.dom.firstChild;
35780         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35781         /*
35782         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35783         this.closeBtn.enableDisplayMode();
35784         this.closeBtn.on("click", this.closeClicked, this);
35785         this.closeBtn.hide();
35786     */
35787         this.createBody(this.config);
35788         if(this.config.hideWhenEmpty){
35789             this.hide();
35790             this.on("paneladded", this.validateVisibility, this);
35791             this.on("panelremoved", this.validateVisibility, this);
35792         }
35793         if(this.autoScroll){
35794             this.bodyEl.setStyle("overflow", "auto");
35795         }else{
35796             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35797         }
35798         //if(c.titlebar !== false){
35799             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35800                 this.titleEl.hide();
35801             }else{
35802                 this.titleEl.show();
35803                 if(this.config.title){
35804                     this.titleTextEl.innerHTML = this.config.title;
35805                 }
35806             }
35807         //}
35808         if(this.config.collapsed){
35809             this.collapse(true);
35810         }
35811         if(this.config.hidden){
35812             this.hide();
35813         }
35814         
35815         if (this.unrendered_panels && this.unrendered_panels.length) {
35816             for (var i =0;i< this.unrendered_panels.length; i++) {
35817                 this.add(this.unrendered_panels[i]);
35818             }
35819             this.unrendered_panels = null;
35820             
35821         }
35822         
35823     },
35824     
35825     applyConfig : function(c)
35826     {
35827         /*
35828          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35829             var dh = Roo.DomHelper;
35830             if(c.titlebar !== false){
35831                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35832                 this.collapseBtn.on("click", this.collapse, this);
35833                 this.collapseBtn.enableDisplayMode();
35834                 /*
35835                 if(c.showPin === true || this.showPin){
35836                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35837                     this.stickBtn.enableDisplayMode();
35838                     this.stickBtn.on("click", this.expand, this);
35839                     this.stickBtn.hide();
35840                 }
35841                 
35842             }
35843             */
35844             /** This region's collapsed element
35845             * @type Roo.Element */
35846             /*
35847              *
35848             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35849                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35850             ]}, true);
35851             
35852             if(c.floatable !== false){
35853                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35854                this.collapsedEl.on("click", this.collapseClick, this);
35855             }
35856
35857             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35858                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35859                    id: "message", unselectable: "on", style:{"float":"left"}});
35860                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35861              }
35862             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35863             this.expandBtn.on("click", this.expand, this);
35864             
35865         }
35866         
35867         if(this.collapseBtn){
35868             this.collapseBtn.setVisible(c.collapsible == true);
35869         }
35870         
35871         this.cmargins = c.cmargins || this.cmargins ||
35872                          (this.position == "west" || this.position == "east" ?
35873                              {top: 0, left: 2, right:2, bottom: 0} :
35874                              {top: 2, left: 0, right:0, bottom: 2});
35875         */
35876         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35877         
35878         
35879         this.bottomTabs = c.tabPosition != "top";
35880         
35881         this.autoScroll = c.autoScroll || false;
35882         
35883         
35884        
35885         
35886         this.duration = c.duration || .30;
35887         this.slideDuration = c.slideDuration || .45;
35888         this.config = c;
35889        
35890     },
35891     /**
35892      * Returns true if this region is currently visible.
35893      * @return {Boolean}
35894      */
35895     isVisible : function(){
35896         return this.visible;
35897     },
35898
35899     /**
35900      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35901      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35902      */
35903     //setCollapsedTitle : function(title){
35904     //    title = title || "&#160;";
35905      //   if(this.collapsedTitleTextEl){
35906       //      this.collapsedTitleTextEl.innerHTML = title;
35907        // }
35908     //},
35909
35910     getBox : function(){
35911         var b;
35912       //  if(!this.collapsed){
35913             b = this.el.getBox(false, true);
35914        // }else{
35915           //  b = this.collapsedEl.getBox(false, true);
35916         //}
35917         return b;
35918     },
35919
35920     getMargins : function(){
35921         return this.margins;
35922         //return this.collapsed ? this.cmargins : this.margins;
35923     },
35924 /*
35925     highlight : function(){
35926         this.el.addClass("x-layout-panel-dragover");
35927     },
35928
35929     unhighlight : function(){
35930         this.el.removeClass("x-layout-panel-dragover");
35931     },
35932 */
35933     updateBox : function(box)
35934     {
35935         if (!this.bodyEl) {
35936             return; // not rendered yet..
35937         }
35938         
35939         this.box = box;
35940         if(!this.collapsed){
35941             this.el.dom.style.left = box.x + "px";
35942             this.el.dom.style.top = box.y + "px";
35943             this.updateBody(box.width, box.height);
35944         }else{
35945             this.collapsedEl.dom.style.left = box.x + "px";
35946             this.collapsedEl.dom.style.top = box.y + "px";
35947             this.collapsedEl.setSize(box.width, box.height);
35948         }
35949         if(this.tabs){
35950             this.tabs.autoSizeTabs();
35951         }
35952     },
35953
35954     updateBody : function(w, h)
35955     {
35956         if(w !== null){
35957             this.el.setWidth(w);
35958             w -= this.el.getBorderWidth("rl");
35959             if(this.config.adjustments){
35960                 w += this.config.adjustments[0];
35961             }
35962         }
35963         if(h !== null && h > 0){
35964             this.el.setHeight(h);
35965             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35966             h -= this.el.getBorderWidth("tb");
35967             if(this.config.adjustments){
35968                 h += this.config.adjustments[1];
35969             }
35970             this.bodyEl.setHeight(h);
35971             if(this.tabs){
35972                 h = this.tabs.syncHeight(h);
35973             }
35974         }
35975         if(this.panelSize){
35976             w = w !== null ? w : this.panelSize.width;
35977             h = h !== null ? h : this.panelSize.height;
35978         }
35979         if(this.activePanel){
35980             var el = this.activePanel.getEl();
35981             w = w !== null ? w : el.getWidth();
35982             h = h !== null ? h : el.getHeight();
35983             this.panelSize = {width: w, height: h};
35984             this.activePanel.setSize(w, h);
35985         }
35986         if(Roo.isIE && this.tabs){
35987             this.tabs.el.repaint();
35988         }
35989     },
35990
35991     /**
35992      * Returns the container element for this region.
35993      * @return {Roo.Element}
35994      */
35995     getEl : function(){
35996         return this.el;
35997     },
35998
35999     /**
36000      * Hides this region.
36001      */
36002     hide : function(){
36003         //if(!this.collapsed){
36004             this.el.dom.style.left = "-2000px";
36005             this.el.hide();
36006         //}else{
36007          //   this.collapsedEl.dom.style.left = "-2000px";
36008          //   this.collapsedEl.hide();
36009        // }
36010         this.visible = false;
36011         this.fireEvent("visibilitychange", this, false);
36012     },
36013
36014     /**
36015      * Shows this region if it was previously hidden.
36016      */
36017     show : function(){
36018         //if(!this.collapsed){
36019             this.el.show();
36020         //}else{
36021         //    this.collapsedEl.show();
36022        // }
36023         this.visible = true;
36024         this.fireEvent("visibilitychange", this, true);
36025     },
36026 /*
36027     closeClicked : function(){
36028         if(this.activePanel){
36029             this.remove(this.activePanel);
36030         }
36031     },
36032
36033     collapseClick : function(e){
36034         if(this.isSlid){
36035            e.stopPropagation();
36036            this.slideIn();
36037         }else{
36038            e.stopPropagation();
36039            this.slideOut();
36040         }
36041     },
36042 */
36043     /**
36044      * Collapses this region.
36045      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36046      */
36047     /*
36048     collapse : function(skipAnim, skipCheck = false){
36049         if(this.collapsed) {
36050             return;
36051         }
36052         
36053         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36054             
36055             this.collapsed = true;
36056             if(this.split){
36057                 this.split.el.hide();
36058             }
36059             if(this.config.animate && skipAnim !== true){
36060                 this.fireEvent("invalidated", this);
36061                 this.animateCollapse();
36062             }else{
36063                 this.el.setLocation(-20000,-20000);
36064                 this.el.hide();
36065                 this.collapsedEl.show();
36066                 this.fireEvent("collapsed", this);
36067                 this.fireEvent("invalidated", this);
36068             }
36069         }
36070         
36071     },
36072 */
36073     animateCollapse : function(){
36074         // overridden
36075     },
36076
36077     /**
36078      * Expands this region if it was previously collapsed.
36079      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36080      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36081      */
36082     /*
36083     expand : function(e, skipAnim){
36084         if(e) {
36085             e.stopPropagation();
36086         }
36087         if(!this.collapsed || this.el.hasActiveFx()) {
36088             return;
36089         }
36090         if(this.isSlid){
36091             this.afterSlideIn();
36092             skipAnim = true;
36093         }
36094         this.collapsed = false;
36095         if(this.config.animate && skipAnim !== true){
36096             this.animateExpand();
36097         }else{
36098             this.el.show();
36099             if(this.split){
36100                 this.split.el.show();
36101             }
36102             this.collapsedEl.setLocation(-2000,-2000);
36103             this.collapsedEl.hide();
36104             this.fireEvent("invalidated", this);
36105             this.fireEvent("expanded", this);
36106         }
36107     },
36108 */
36109     animateExpand : function(){
36110         // overridden
36111     },
36112
36113     initTabs : function()
36114     {
36115         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36116         
36117         var ts = new Roo.bootstrap.panel.Tabs({
36118                 el: this.bodyEl.dom,
36119                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36120                 disableTooltips: this.config.disableTabTips,
36121                 toolbar : this.config.toolbar
36122             });
36123         
36124         if(this.config.hideTabs){
36125             ts.stripWrap.setDisplayed(false);
36126         }
36127         this.tabs = ts;
36128         ts.resizeTabs = this.config.resizeTabs === true;
36129         ts.minTabWidth = this.config.minTabWidth || 40;
36130         ts.maxTabWidth = this.config.maxTabWidth || 250;
36131         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36132         ts.monitorResize = false;
36133         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36134         ts.bodyEl.addClass('roo-layout-tabs-body');
36135         this.panels.each(this.initPanelAsTab, this);
36136     },
36137
36138     initPanelAsTab : function(panel){
36139         var ti = this.tabs.addTab(
36140             panel.getEl().id,
36141             panel.getTitle(),
36142             null,
36143             this.config.closeOnTab && panel.isClosable(),
36144             panel.tpl
36145         );
36146         if(panel.tabTip !== undefined){
36147             ti.setTooltip(panel.tabTip);
36148         }
36149         ti.on("activate", function(){
36150               this.setActivePanel(panel);
36151         }, this);
36152         
36153         if(this.config.closeOnTab){
36154             ti.on("beforeclose", function(t, e){
36155                 e.cancel = true;
36156                 this.remove(panel);
36157             }, this);
36158         }
36159         
36160         panel.tabItem = ti;
36161         
36162         return ti;
36163     },
36164
36165     updatePanelTitle : function(panel, title)
36166     {
36167         if(this.activePanel == panel){
36168             this.updateTitle(title);
36169         }
36170         if(this.tabs){
36171             var ti = this.tabs.getTab(panel.getEl().id);
36172             ti.setText(title);
36173             if(panel.tabTip !== undefined){
36174                 ti.setTooltip(panel.tabTip);
36175             }
36176         }
36177     },
36178
36179     updateTitle : function(title){
36180         if(this.titleTextEl && !this.config.title){
36181             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36182         }
36183     },
36184
36185     setActivePanel : function(panel)
36186     {
36187         panel = this.getPanel(panel);
36188         if(this.activePanel && this.activePanel != panel){
36189             if(this.activePanel.setActiveState(false) === false){
36190                 return;
36191             }
36192         }
36193         this.activePanel = panel;
36194         panel.setActiveState(true);
36195         if(this.panelSize){
36196             panel.setSize(this.panelSize.width, this.panelSize.height);
36197         }
36198         if(this.closeBtn){
36199             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36200         }
36201         this.updateTitle(panel.getTitle());
36202         if(this.tabs){
36203             this.fireEvent("invalidated", this);
36204         }
36205         this.fireEvent("panelactivated", this, panel);
36206     },
36207
36208     /**
36209      * Shows the specified panel.
36210      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36211      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36212      */
36213     showPanel : function(panel)
36214     {
36215         panel = this.getPanel(panel);
36216         if(panel){
36217             if(this.tabs){
36218                 var tab = this.tabs.getTab(panel.getEl().id);
36219                 if(tab.isHidden()){
36220                     this.tabs.unhideTab(tab.id);
36221                 }
36222                 tab.activate();
36223             }else{
36224                 this.setActivePanel(panel);
36225             }
36226         }
36227         return panel;
36228     },
36229
36230     /**
36231      * Get the active panel for this region.
36232      * @return {Roo.ContentPanel} The active panel or null
36233      */
36234     getActivePanel : function(){
36235         return this.activePanel;
36236     },
36237
36238     validateVisibility : function(){
36239         if(this.panels.getCount() < 1){
36240             this.updateTitle("&#160;");
36241             this.closeBtn.hide();
36242             this.hide();
36243         }else{
36244             if(!this.isVisible()){
36245                 this.show();
36246             }
36247         }
36248     },
36249
36250     /**
36251      * Adds the passed ContentPanel(s) to this region.
36252      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36253      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36254      */
36255     add : function(panel)
36256     {
36257         if(arguments.length > 1){
36258             for(var i = 0, len = arguments.length; i < len; i++) {
36259                 this.add(arguments[i]);
36260             }
36261             return null;
36262         }
36263         
36264         // if we have not been rendered yet, then we can not really do much of this..
36265         if (!this.bodyEl) {
36266             this.unrendered_panels.push(panel);
36267             return panel;
36268         }
36269         
36270         
36271         
36272         
36273         if(this.hasPanel(panel)){
36274             this.showPanel(panel);
36275             return panel;
36276         }
36277         panel.setRegion(this);
36278         this.panels.add(panel);
36279        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36280             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36281             // and hide them... ???
36282             this.bodyEl.dom.appendChild(panel.getEl().dom);
36283             if(panel.background !== true){
36284                 this.setActivePanel(panel);
36285             }
36286             this.fireEvent("paneladded", this, panel);
36287             return panel;
36288         }
36289         */
36290         if(!this.tabs){
36291             this.initTabs();
36292         }else{
36293             this.initPanelAsTab(panel);
36294         }
36295         
36296         
36297         if(panel.background !== true){
36298             this.tabs.activate(panel.getEl().id);
36299         }
36300         this.fireEvent("paneladded", this, panel);
36301         return panel;
36302     },
36303
36304     /**
36305      * Hides the tab for the specified panel.
36306      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36307      */
36308     hidePanel : function(panel){
36309         if(this.tabs && (panel = this.getPanel(panel))){
36310             this.tabs.hideTab(panel.getEl().id);
36311         }
36312     },
36313
36314     /**
36315      * Unhides the tab for a previously hidden panel.
36316      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36317      */
36318     unhidePanel : function(panel){
36319         if(this.tabs && (panel = this.getPanel(panel))){
36320             this.tabs.unhideTab(panel.getEl().id);
36321         }
36322     },
36323
36324     clearPanels : function(){
36325         while(this.panels.getCount() > 0){
36326              this.remove(this.panels.first());
36327         }
36328     },
36329
36330     /**
36331      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36332      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36333      * @param {Boolean} preservePanel Overrides the config preservePanel option
36334      * @return {Roo.ContentPanel} The panel that was removed
36335      */
36336     remove : function(panel, preservePanel)
36337     {
36338         panel = this.getPanel(panel);
36339         if(!panel){
36340             return null;
36341         }
36342         var e = {};
36343         this.fireEvent("beforeremove", this, panel, e);
36344         if(e.cancel === true){
36345             return null;
36346         }
36347         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36348         var panelId = panel.getId();
36349         this.panels.removeKey(panelId);
36350         if(preservePanel){
36351             document.body.appendChild(panel.getEl().dom);
36352         }
36353         if(this.tabs){
36354             this.tabs.removeTab(panel.getEl().id);
36355         }else if (!preservePanel){
36356             this.bodyEl.dom.removeChild(panel.getEl().dom);
36357         }
36358         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36359             var p = this.panels.first();
36360             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36361             tempEl.appendChild(p.getEl().dom);
36362             this.bodyEl.update("");
36363             this.bodyEl.dom.appendChild(p.getEl().dom);
36364             tempEl = null;
36365             this.updateTitle(p.getTitle());
36366             this.tabs = null;
36367             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36368             this.setActivePanel(p);
36369         }
36370         panel.setRegion(null);
36371         if(this.activePanel == panel){
36372             this.activePanel = null;
36373         }
36374         if(this.config.autoDestroy !== false && preservePanel !== true){
36375             try{panel.destroy();}catch(e){}
36376         }
36377         this.fireEvent("panelremoved", this, panel);
36378         return panel;
36379     },
36380
36381     /**
36382      * Returns the TabPanel component used by this region
36383      * @return {Roo.TabPanel}
36384      */
36385     getTabs : function(){
36386         return this.tabs;
36387     },
36388
36389     createTool : function(parentEl, className){
36390         var btn = Roo.DomHelper.append(parentEl, {
36391             tag: "div",
36392             cls: "x-layout-tools-button",
36393             children: [ {
36394                 tag: "div",
36395                 cls: "roo-layout-tools-button-inner " + className,
36396                 html: "&#160;"
36397             }]
36398         }, true);
36399         btn.addClassOnOver("roo-layout-tools-button-over");
36400         return btn;
36401     }
36402 });/*
36403  * Based on:
36404  * Ext JS Library 1.1.1
36405  * Copyright(c) 2006-2007, Ext JS, LLC.
36406  *
36407  * Originally Released Under LGPL - original licence link has changed is not relivant.
36408  *
36409  * Fork - LGPL
36410  * <script type="text/javascript">
36411  */
36412  
36413
36414
36415 /**
36416  * @class Roo.SplitLayoutRegion
36417  * @extends Roo.LayoutRegion
36418  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36419  */
36420 Roo.bootstrap.layout.Split = function(config){
36421     this.cursor = config.cursor;
36422     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36423 };
36424
36425 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36426 {
36427     splitTip : "Drag to resize.",
36428     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36429     useSplitTips : false,
36430
36431     applyConfig : function(config){
36432         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36433     },
36434     
36435     onRender : function(ctr,pos) {
36436         
36437         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36438         if(!this.config.split){
36439             return;
36440         }
36441         if(!this.split){
36442             
36443             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36444                             tag: "div",
36445                             id: this.el.id + "-split",
36446                             cls: "roo-layout-split roo-layout-split-"+this.position,
36447                             html: "&#160;"
36448             });
36449             /** The SplitBar for this region 
36450             * @type Roo.SplitBar */
36451             // does not exist yet...
36452             Roo.log([this.position, this.orientation]);
36453             
36454             this.split = new Roo.bootstrap.SplitBar({
36455                 dragElement : splitEl,
36456                 resizingElement: this.el,
36457                 orientation : this.orientation
36458             });
36459             
36460             this.split.on("moved", this.onSplitMove, this);
36461             this.split.useShim = this.config.useShim === true;
36462             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36463             if(this.useSplitTips){
36464                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36465             }
36466             //if(config.collapsible){
36467             //    this.split.el.on("dblclick", this.collapse,  this);
36468             //}
36469         }
36470         if(typeof this.config.minSize != "undefined"){
36471             this.split.minSize = this.config.minSize;
36472         }
36473         if(typeof this.config.maxSize != "undefined"){
36474             this.split.maxSize = this.config.maxSize;
36475         }
36476         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36477             this.hideSplitter();
36478         }
36479         
36480     },
36481
36482     getHMaxSize : function(){
36483          var cmax = this.config.maxSize || 10000;
36484          var center = this.mgr.getRegion("center");
36485          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36486     },
36487
36488     getVMaxSize : function(){
36489          var cmax = this.config.maxSize || 10000;
36490          var center = this.mgr.getRegion("center");
36491          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36492     },
36493
36494     onSplitMove : function(split, newSize){
36495         this.fireEvent("resized", this, newSize);
36496     },
36497     
36498     /** 
36499      * Returns the {@link Roo.SplitBar} for this region.
36500      * @return {Roo.SplitBar}
36501      */
36502     getSplitBar : function(){
36503         return this.split;
36504     },
36505     
36506     hide : function(){
36507         this.hideSplitter();
36508         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36509     },
36510
36511     hideSplitter : function(){
36512         if(this.split){
36513             this.split.el.setLocation(-2000,-2000);
36514             this.split.el.hide();
36515         }
36516     },
36517
36518     show : function(){
36519         if(this.split){
36520             this.split.el.show();
36521         }
36522         Roo.bootstrap.layout.Split.superclass.show.call(this);
36523     },
36524     
36525     beforeSlide: function(){
36526         if(Roo.isGecko){// firefox overflow auto bug workaround
36527             this.bodyEl.clip();
36528             if(this.tabs) {
36529                 this.tabs.bodyEl.clip();
36530             }
36531             if(this.activePanel){
36532                 this.activePanel.getEl().clip();
36533                 
36534                 if(this.activePanel.beforeSlide){
36535                     this.activePanel.beforeSlide();
36536                 }
36537             }
36538         }
36539     },
36540     
36541     afterSlide : function(){
36542         if(Roo.isGecko){// firefox overflow auto bug workaround
36543             this.bodyEl.unclip();
36544             if(this.tabs) {
36545                 this.tabs.bodyEl.unclip();
36546             }
36547             if(this.activePanel){
36548                 this.activePanel.getEl().unclip();
36549                 if(this.activePanel.afterSlide){
36550                     this.activePanel.afterSlide();
36551                 }
36552             }
36553         }
36554     },
36555
36556     initAutoHide : function(){
36557         if(this.autoHide !== false){
36558             if(!this.autoHideHd){
36559                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36560                 this.autoHideHd = {
36561                     "mouseout": function(e){
36562                         if(!e.within(this.el, true)){
36563                             st.delay(500);
36564                         }
36565                     },
36566                     "mouseover" : function(e){
36567                         st.cancel();
36568                     },
36569                     scope : this
36570                 };
36571             }
36572             this.el.on(this.autoHideHd);
36573         }
36574     },
36575
36576     clearAutoHide : function(){
36577         if(this.autoHide !== false){
36578             this.el.un("mouseout", this.autoHideHd.mouseout);
36579             this.el.un("mouseover", this.autoHideHd.mouseover);
36580         }
36581     },
36582
36583     clearMonitor : function(){
36584         Roo.get(document).un("click", this.slideInIf, this);
36585     },
36586
36587     // these names are backwards but not changed for compat
36588     slideOut : function(){
36589         if(this.isSlid || this.el.hasActiveFx()){
36590             return;
36591         }
36592         this.isSlid = true;
36593         if(this.collapseBtn){
36594             this.collapseBtn.hide();
36595         }
36596         this.closeBtnState = this.closeBtn.getStyle('display');
36597         this.closeBtn.hide();
36598         if(this.stickBtn){
36599             this.stickBtn.show();
36600         }
36601         this.el.show();
36602         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36603         this.beforeSlide();
36604         this.el.setStyle("z-index", 10001);
36605         this.el.slideIn(this.getSlideAnchor(), {
36606             callback: function(){
36607                 this.afterSlide();
36608                 this.initAutoHide();
36609                 Roo.get(document).on("click", this.slideInIf, this);
36610                 this.fireEvent("slideshow", this);
36611             },
36612             scope: this,
36613             block: true
36614         });
36615     },
36616
36617     afterSlideIn : function(){
36618         this.clearAutoHide();
36619         this.isSlid = false;
36620         this.clearMonitor();
36621         this.el.setStyle("z-index", "");
36622         if(this.collapseBtn){
36623             this.collapseBtn.show();
36624         }
36625         this.closeBtn.setStyle('display', this.closeBtnState);
36626         if(this.stickBtn){
36627             this.stickBtn.hide();
36628         }
36629         this.fireEvent("slidehide", this);
36630     },
36631
36632     slideIn : function(cb){
36633         if(!this.isSlid || this.el.hasActiveFx()){
36634             Roo.callback(cb);
36635             return;
36636         }
36637         this.isSlid = false;
36638         this.beforeSlide();
36639         this.el.slideOut(this.getSlideAnchor(), {
36640             callback: function(){
36641                 this.el.setLeftTop(-10000, -10000);
36642                 this.afterSlide();
36643                 this.afterSlideIn();
36644                 Roo.callback(cb);
36645             },
36646             scope: this,
36647             block: true
36648         });
36649     },
36650     
36651     slideInIf : function(e){
36652         if(!e.within(this.el)){
36653             this.slideIn();
36654         }
36655     },
36656
36657     animateCollapse : function(){
36658         this.beforeSlide();
36659         this.el.setStyle("z-index", 20000);
36660         var anchor = this.getSlideAnchor();
36661         this.el.slideOut(anchor, {
36662             callback : function(){
36663                 this.el.setStyle("z-index", "");
36664                 this.collapsedEl.slideIn(anchor, {duration:.3});
36665                 this.afterSlide();
36666                 this.el.setLocation(-10000,-10000);
36667                 this.el.hide();
36668                 this.fireEvent("collapsed", this);
36669             },
36670             scope: this,
36671             block: true
36672         });
36673     },
36674
36675     animateExpand : function(){
36676         this.beforeSlide();
36677         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36678         this.el.setStyle("z-index", 20000);
36679         this.collapsedEl.hide({
36680             duration:.1
36681         });
36682         this.el.slideIn(this.getSlideAnchor(), {
36683             callback : function(){
36684                 this.el.setStyle("z-index", "");
36685                 this.afterSlide();
36686                 if(this.split){
36687                     this.split.el.show();
36688                 }
36689                 this.fireEvent("invalidated", this);
36690                 this.fireEvent("expanded", this);
36691             },
36692             scope: this,
36693             block: true
36694         });
36695     },
36696
36697     anchors : {
36698         "west" : "left",
36699         "east" : "right",
36700         "north" : "top",
36701         "south" : "bottom"
36702     },
36703
36704     sanchors : {
36705         "west" : "l",
36706         "east" : "r",
36707         "north" : "t",
36708         "south" : "b"
36709     },
36710
36711     canchors : {
36712         "west" : "tl-tr",
36713         "east" : "tr-tl",
36714         "north" : "tl-bl",
36715         "south" : "bl-tl"
36716     },
36717
36718     getAnchor : function(){
36719         return this.anchors[this.position];
36720     },
36721
36722     getCollapseAnchor : function(){
36723         return this.canchors[this.position];
36724     },
36725
36726     getSlideAnchor : function(){
36727         return this.sanchors[this.position];
36728     },
36729
36730     getAlignAdj : function(){
36731         var cm = this.cmargins;
36732         switch(this.position){
36733             case "west":
36734                 return [0, 0];
36735             break;
36736             case "east":
36737                 return [0, 0];
36738             break;
36739             case "north":
36740                 return [0, 0];
36741             break;
36742             case "south":
36743                 return [0, 0];
36744             break;
36745         }
36746     },
36747
36748     getExpandAdj : function(){
36749         var c = this.collapsedEl, cm = this.cmargins;
36750         switch(this.position){
36751             case "west":
36752                 return [-(cm.right+c.getWidth()+cm.left), 0];
36753             break;
36754             case "east":
36755                 return [cm.right+c.getWidth()+cm.left, 0];
36756             break;
36757             case "north":
36758                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36759             break;
36760             case "south":
36761                 return [0, cm.top+cm.bottom+c.getHeight()];
36762             break;
36763         }
36764     }
36765 });/*
36766  * Based on:
36767  * Ext JS Library 1.1.1
36768  * Copyright(c) 2006-2007, Ext JS, LLC.
36769  *
36770  * Originally Released Under LGPL - original licence link has changed is not relivant.
36771  *
36772  * Fork - LGPL
36773  * <script type="text/javascript">
36774  */
36775 /*
36776  * These classes are private internal classes
36777  */
36778 Roo.bootstrap.layout.Center = function(config){
36779     config.region = "center";
36780     Roo.bootstrap.layout.Region.call(this, config);
36781     this.visible = true;
36782     this.minWidth = config.minWidth || 20;
36783     this.minHeight = config.minHeight || 20;
36784 };
36785
36786 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36787     hide : function(){
36788         // center panel can't be hidden
36789     },
36790     
36791     show : function(){
36792         // center panel can't be hidden
36793     },
36794     
36795     getMinWidth: function(){
36796         return this.minWidth;
36797     },
36798     
36799     getMinHeight: function(){
36800         return this.minHeight;
36801     }
36802 });
36803
36804
36805
36806
36807  
36808
36809
36810
36811
36812
36813 Roo.bootstrap.layout.North = function(config)
36814 {
36815     config.region = 'north';
36816     config.cursor = 'n-resize';
36817     
36818     Roo.bootstrap.layout.Split.call(this, config);
36819     
36820     
36821     if(this.split){
36822         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36823         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36824         this.split.el.addClass("roo-layout-split-v");
36825     }
36826     var size = config.initialSize || config.height;
36827     if(typeof size != "undefined"){
36828         this.el.setHeight(size);
36829     }
36830 };
36831 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36832 {
36833     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36834     
36835     
36836     
36837     getBox : function(){
36838         if(this.collapsed){
36839             return this.collapsedEl.getBox();
36840         }
36841         var box = this.el.getBox();
36842         if(this.split){
36843             box.height += this.split.el.getHeight();
36844         }
36845         return box;
36846     },
36847     
36848     updateBox : function(box){
36849         if(this.split && !this.collapsed){
36850             box.height -= this.split.el.getHeight();
36851             this.split.el.setLeft(box.x);
36852             this.split.el.setTop(box.y+box.height);
36853             this.split.el.setWidth(box.width);
36854         }
36855         if(this.collapsed){
36856             this.updateBody(box.width, null);
36857         }
36858         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36859     }
36860 });
36861
36862
36863
36864
36865
36866 Roo.bootstrap.layout.South = function(config){
36867     config.region = 'south';
36868     config.cursor = 's-resize';
36869     Roo.bootstrap.layout.Split.call(this, config);
36870     if(this.split){
36871         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36872         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36873         this.split.el.addClass("roo-layout-split-v");
36874     }
36875     var size = config.initialSize || config.height;
36876     if(typeof size != "undefined"){
36877         this.el.setHeight(size);
36878     }
36879 };
36880
36881 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36882     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36883     getBox : function(){
36884         if(this.collapsed){
36885             return this.collapsedEl.getBox();
36886         }
36887         var box = this.el.getBox();
36888         if(this.split){
36889             var sh = this.split.el.getHeight();
36890             box.height += sh;
36891             box.y -= sh;
36892         }
36893         return box;
36894     },
36895     
36896     updateBox : function(box){
36897         if(this.split && !this.collapsed){
36898             var sh = this.split.el.getHeight();
36899             box.height -= sh;
36900             box.y += sh;
36901             this.split.el.setLeft(box.x);
36902             this.split.el.setTop(box.y-sh);
36903             this.split.el.setWidth(box.width);
36904         }
36905         if(this.collapsed){
36906             this.updateBody(box.width, null);
36907         }
36908         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36909     }
36910 });
36911
36912 Roo.bootstrap.layout.East = function(config){
36913     config.region = "east";
36914     config.cursor = "e-resize";
36915     Roo.bootstrap.layout.Split.call(this, config);
36916     if(this.split){
36917         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36918         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36919         this.split.el.addClass("roo-layout-split-h");
36920     }
36921     var size = config.initialSize || config.width;
36922     if(typeof size != "undefined"){
36923         this.el.setWidth(size);
36924     }
36925 };
36926 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36927     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36928     getBox : function(){
36929         if(this.collapsed){
36930             return this.collapsedEl.getBox();
36931         }
36932         var box = this.el.getBox();
36933         if(this.split){
36934             var sw = this.split.el.getWidth();
36935             box.width += sw;
36936             box.x -= sw;
36937         }
36938         return box;
36939     },
36940
36941     updateBox : function(box){
36942         if(this.split && !this.collapsed){
36943             var sw = this.split.el.getWidth();
36944             box.width -= sw;
36945             this.split.el.setLeft(box.x);
36946             this.split.el.setTop(box.y);
36947             this.split.el.setHeight(box.height);
36948             box.x += sw;
36949         }
36950         if(this.collapsed){
36951             this.updateBody(null, box.height);
36952         }
36953         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36954     }
36955 });
36956
36957 Roo.bootstrap.layout.West = function(config){
36958     config.region = "west";
36959     config.cursor = "w-resize";
36960     
36961     Roo.bootstrap.layout.Split.call(this, config);
36962     if(this.split){
36963         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36964         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36965         this.split.el.addClass("roo-layout-split-h");
36966     }
36967     
36968 };
36969 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36970     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36971     
36972     onRender: function(ctr, pos)
36973     {
36974         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36975         var size = this.config.initialSize || this.config.width;
36976         if(typeof size != "undefined"){
36977             this.el.setWidth(size);
36978         }
36979     },
36980     
36981     getBox : function(){
36982         if(this.collapsed){
36983             return this.collapsedEl.getBox();
36984         }
36985         var box = this.el.getBox();
36986         if(this.split){
36987             box.width += this.split.el.getWidth();
36988         }
36989         return box;
36990     },
36991     
36992     updateBox : function(box){
36993         if(this.split && !this.collapsed){
36994             var sw = this.split.el.getWidth();
36995             box.width -= sw;
36996             this.split.el.setLeft(box.x+box.width);
36997             this.split.el.setTop(box.y);
36998             this.split.el.setHeight(box.height);
36999         }
37000         if(this.collapsed){
37001             this.updateBody(null, box.height);
37002         }
37003         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37004     }
37005 });
37006 Roo.namespace("Roo.bootstrap.panel");/*
37007  * Based on:
37008  * Ext JS Library 1.1.1
37009  * Copyright(c) 2006-2007, Ext JS, LLC.
37010  *
37011  * Originally Released Under LGPL - original licence link has changed is not relivant.
37012  *
37013  * Fork - LGPL
37014  * <script type="text/javascript">
37015  */
37016 /**
37017  * @class Roo.ContentPanel
37018  * @extends Roo.util.Observable
37019  * A basic ContentPanel element.
37020  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37021  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37022  * @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
37023  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37024  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37025  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37026  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37027  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37028  * @cfg {String} title          The title for this panel
37029  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37030  * @cfg {String} url            Calls {@link #setUrl} with this value
37031  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37032  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37033  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37034  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37035  * @cfg {Boolean} badges render the badges
37036
37037  * @constructor
37038  * Create a new ContentPanel.
37039  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37040  * @param {String/Object} config A string to set only the title or a config object
37041  * @param {String} content (optional) Set the HTML content for this panel
37042  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37043  */
37044 Roo.bootstrap.panel.Content = function( config){
37045     
37046     this.tpl = config.tpl || false;
37047     
37048     var el = config.el;
37049     var content = config.content;
37050
37051     if(config.autoCreate){ // xtype is available if this is called from factory
37052         el = Roo.id();
37053     }
37054     this.el = Roo.get(el);
37055     if(!this.el && config && config.autoCreate){
37056         if(typeof config.autoCreate == "object"){
37057             if(!config.autoCreate.id){
37058                 config.autoCreate.id = config.id||el;
37059             }
37060             this.el = Roo.DomHelper.append(document.body,
37061                         config.autoCreate, true);
37062         }else{
37063             var elcfg =  {   tag: "div",
37064                             cls: "roo-layout-inactive-content",
37065                             id: config.id||el
37066                             };
37067             if (config.html) {
37068                 elcfg.html = config.html;
37069                 
37070             }
37071                         
37072             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37073         }
37074     } 
37075     this.closable = false;
37076     this.loaded = false;
37077     this.active = false;
37078    
37079       
37080     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37081         
37082         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37083         
37084         this.wrapEl = this.el; //this.el.wrap();
37085         var ti = [];
37086         if (config.toolbar.items) {
37087             ti = config.toolbar.items ;
37088             delete config.toolbar.items ;
37089         }
37090         
37091         var nitems = [];
37092         this.toolbar.render(this.wrapEl, 'before');
37093         for(var i =0;i < ti.length;i++) {
37094           //  Roo.log(['add child', items[i]]);
37095             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37096         }
37097         this.toolbar.items = nitems;
37098         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37099         delete config.toolbar;
37100         
37101     }
37102     /*
37103     // xtype created footer. - not sure if will work as we normally have to render first..
37104     if (this.footer && !this.footer.el && this.footer.xtype) {
37105         if (!this.wrapEl) {
37106             this.wrapEl = this.el.wrap();
37107         }
37108     
37109         this.footer.container = this.wrapEl.createChild();
37110          
37111         this.footer = Roo.factory(this.footer, Roo);
37112         
37113     }
37114     */
37115     
37116      if(typeof config == "string"){
37117         this.title = config;
37118     }else{
37119         Roo.apply(this, config);
37120     }
37121     
37122     if(this.resizeEl){
37123         this.resizeEl = Roo.get(this.resizeEl, true);
37124     }else{
37125         this.resizeEl = this.el;
37126     }
37127     // handle view.xtype
37128     
37129  
37130     
37131     
37132     this.addEvents({
37133         /**
37134          * @event activate
37135          * Fires when this panel is activated. 
37136          * @param {Roo.ContentPanel} this
37137          */
37138         "activate" : true,
37139         /**
37140          * @event deactivate
37141          * Fires when this panel is activated. 
37142          * @param {Roo.ContentPanel} this
37143          */
37144         "deactivate" : true,
37145
37146         /**
37147          * @event resize
37148          * Fires when this panel is resized if fitToFrame is true.
37149          * @param {Roo.ContentPanel} this
37150          * @param {Number} width The width after any component adjustments
37151          * @param {Number} height The height after any component adjustments
37152          */
37153         "resize" : true,
37154         
37155          /**
37156          * @event render
37157          * Fires when this tab is created
37158          * @param {Roo.ContentPanel} this
37159          */
37160         "render" : true
37161         
37162         
37163         
37164     });
37165     
37166
37167     
37168     
37169     if(this.autoScroll){
37170         this.resizeEl.setStyle("overflow", "auto");
37171     } else {
37172         // fix randome scrolling
37173         //this.el.on('scroll', function() {
37174         //    Roo.log('fix random scolling');
37175         //    this.scrollTo('top',0); 
37176         //});
37177     }
37178     content = content || this.content;
37179     if(content){
37180         this.setContent(content);
37181     }
37182     if(config && config.url){
37183         this.setUrl(this.url, this.params, this.loadOnce);
37184     }
37185     
37186     
37187     
37188     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37189     
37190     if (this.view && typeof(this.view.xtype) != 'undefined') {
37191         this.view.el = this.el.appendChild(document.createElement("div"));
37192         this.view = Roo.factory(this.view); 
37193         this.view.render  &&  this.view.render(false, '');  
37194     }
37195     
37196     
37197     this.fireEvent('render', this);
37198 };
37199
37200 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37201     
37202     tabTip : '',
37203     
37204     setRegion : function(region){
37205         this.region = region;
37206         this.setActiveClass(region && !this.background);
37207     },
37208     
37209     
37210     setActiveClass: function(state)
37211     {
37212         if(state){
37213            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37214            this.el.setStyle('position','relative');
37215         }else{
37216            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37217            this.el.setStyle('position', 'absolute');
37218         } 
37219     },
37220     
37221     /**
37222      * Returns the toolbar for this Panel if one was configured. 
37223      * @return {Roo.Toolbar} 
37224      */
37225     getToolbar : function(){
37226         return this.toolbar;
37227     },
37228     
37229     setActiveState : function(active)
37230     {
37231         this.active = active;
37232         this.setActiveClass(active);
37233         if(!active){
37234             if(this.fireEvent("deactivate", this) === false){
37235                 return false;
37236             }
37237             return true;
37238         }
37239         this.fireEvent("activate", this);
37240         return true;
37241     },
37242     /**
37243      * Updates this panel's element
37244      * @param {String} content The new content
37245      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37246     */
37247     setContent : function(content, loadScripts){
37248         this.el.update(content, loadScripts);
37249     },
37250
37251     ignoreResize : function(w, h){
37252         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37253             return true;
37254         }else{
37255             this.lastSize = {width: w, height: h};
37256             return false;
37257         }
37258     },
37259     /**
37260      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37261      * @return {Roo.UpdateManager} The UpdateManager
37262      */
37263     getUpdateManager : function(){
37264         return this.el.getUpdateManager();
37265     },
37266      /**
37267      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37268      * @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:
37269 <pre><code>
37270 panel.load({
37271     url: "your-url.php",
37272     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37273     callback: yourFunction,
37274     scope: yourObject, //(optional scope)
37275     discardUrl: false,
37276     nocache: false,
37277     text: "Loading...",
37278     timeout: 30,
37279     scripts: false
37280 });
37281 </code></pre>
37282      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37283      * 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.
37284      * @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}
37285      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37286      * @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.
37287      * @return {Roo.ContentPanel} this
37288      */
37289     load : function(){
37290         var um = this.el.getUpdateManager();
37291         um.update.apply(um, arguments);
37292         return this;
37293     },
37294
37295
37296     /**
37297      * 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.
37298      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37299      * @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)
37300      * @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)
37301      * @return {Roo.UpdateManager} The UpdateManager
37302      */
37303     setUrl : function(url, params, loadOnce){
37304         if(this.refreshDelegate){
37305             this.removeListener("activate", this.refreshDelegate);
37306         }
37307         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37308         this.on("activate", this.refreshDelegate);
37309         return this.el.getUpdateManager();
37310     },
37311     
37312     _handleRefresh : function(url, params, loadOnce){
37313         if(!loadOnce || !this.loaded){
37314             var updater = this.el.getUpdateManager();
37315             updater.update(url, params, this._setLoaded.createDelegate(this));
37316         }
37317     },
37318     
37319     _setLoaded : function(){
37320         this.loaded = true;
37321     }, 
37322     
37323     /**
37324      * Returns this panel's id
37325      * @return {String} 
37326      */
37327     getId : function(){
37328         return this.el.id;
37329     },
37330     
37331     /** 
37332      * Returns this panel's element - used by regiosn to add.
37333      * @return {Roo.Element} 
37334      */
37335     getEl : function(){
37336         return this.wrapEl || this.el;
37337     },
37338     
37339    
37340     
37341     adjustForComponents : function(width, height)
37342     {
37343         //Roo.log('adjustForComponents ');
37344         if(this.resizeEl != this.el){
37345             width -= this.el.getFrameWidth('lr');
37346             height -= this.el.getFrameWidth('tb');
37347         }
37348         if(this.toolbar){
37349             var te = this.toolbar.getEl();
37350             te.setWidth(width);
37351             height -= te.getHeight();
37352         }
37353         if(this.footer){
37354             var te = this.footer.getEl();
37355             te.setWidth(width);
37356             height -= te.getHeight();
37357         }
37358         
37359         
37360         if(this.adjustments){
37361             width += this.adjustments[0];
37362             height += this.adjustments[1];
37363         }
37364         return {"width": width, "height": height};
37365     },
37366     
37367     setSize : function(width, height){
37368         if(this.fitToFrame && !this.ignoreResize(width, height)){
37369             if(this.fitContainer && this.resizeEl != this.el){
37370                 this.el.setSize(width, height);
37371             }
37372             var size = this.adjustForComponents(width, height);
37373             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37374             this.fireEvent('resize', this, size.width, size.height);
37375         }
37376     },
37377     
37378     /**
37379      * Returns this panel's title
37380      * @return {String} 
37381      */
37382     getTitle : function(){
37383         
37384         if (typeof(this.title) != 'object') {
37385             return this.title;
37386         }
37387         
37388         var t = '';
37389         for (var k in this.title) {
37390             if (!this.title.hasOwnProperty(k)) {
37391                 continue;
37392             }
37393             
37394             if (k.indexOf('-') >= 0) {
37395                 var s = k.split('-');
37396                 for (var i = 0; i<s.length; i++) {
37397                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37398                 }
37399             } else {
37400                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37401             }
37402         }
37403         return t;
37404     },
37405     
37406     /**
37407      * Set this panel's title
37408      * @param {String} title
37409      */
37410     setTitle : function(title){
37411         this.title = title;
37412         if(this.region){
37413             this.region.updatePanelTitle(this, title);
37414         }
37415     },
37416     
37417     /**
37418      * Returns true is this panel was configured to be closable
37419      * @return {Boolean} 
37420      */
37421     isClosable : function(){
37422         return this.closable;
37423     },
37424     
37425     beforeSlide : function(){
37426         this.el.clip();
37427         this.resizeEl.clip();
37428     },
37429     
37430     afterSlide : function(){
37431         this.el.unclip();
37432         this.resizeEl.unclip();
37433     },
37434     
37435     /**
37436      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37437      *   Will fail silently if the {@link #setUrl} method has not been called.
37438      *   This does not activate the panel, just updates its content.
37439      */
37440     refresh : function(){
37441         if(this.refreshDelegate){
37442            this.loaded = false;
37443            this.refreshDelegate();
37444         }
37445     },
37446     
37447     /**
37448      * Destroys this panel
37449      */
37450     destroy : function(){
37451         this.el.removeAllListeners();
37452         var tempEl = document.createElement("span");
37453         tempEl.appendChild(this.el.dom);
37454         tempEl.innerHTML = "";
37455         this.el.remove();
37456         this.el = null;
37457     },
37458     
37459     /**
37460      * form - if the content panel contains a form - this is a reference to it.
37461      * @type {Roo.form.Form}
37462      */
37463     form : false,
37464     /**
37465      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37466      *    This contains a reference to it.
37467      * @type {Roo.View}
37468      */
37469     view : false,
37470     
37471       /**
37472      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37473      * <pre><code>
37474
37475 layout.addxtype({
37476        xtype : 'Form',
37477        items: [ .... ]
37478    }
37479 );
37480
37481 </code></pre>
37482      * @param {Object} cfg Xtype definition of item to add.
37483      */
37484     
37485     
37486     getChildContainer: function () {
37487         return this.getEl();
37488     }
37489     
37490     
37491     /*
37492         var  ret = new Roo.factory(cfg);
37493         return ret;
37494         
37495         
37496         // add form..
37497         if (cfg.xtype.match(/^Form$/)) {
37498             
37499             var el;
37500             //if (this.footer) {
37501             //    el = this.footer.container.insertSibling(false, 'before');
37502             //} else {
37503                 el = this.el.createChild();
37504             //}
37505
37506             this.form = new  Roo.form.Form(cfg);
37507             
37508             
37509             if ( this.form.allItems.length) {
37510                 this.form.render(el.dom);
37511             }
37512             return this.form;
37513         }
37514         // should only have one of theses..
37515         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37516             // views.. should not be just added - used named prop 'view''
37517             
37518             cfg.el = this.el.appendChild(document.createElement("div"));
37519             // factory?
37520             
37521             var ret = new Roo.factory(cfg);
37522              
37523              ret.render && ret.render(false, ''); // render blank..
37524             this.view = ret;
37525             return ret;
37526         }
37527         return false;
37528     }
37529     \*/
37530 });
37531  
37532 /**
37533  * @class Roo.bootstrap.panel.Grid
37534  * @extends Roo.bootstrap.panel.Content
37535  * @constructor
37536  * Create a new GridPanel.
37537  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37538  * @param {Object} config A the config object
37539   
37540  */
37541
37542
37543
37544 Roo.bootstrap.panel.Grid = function(config)
37545 {
37546     
37547       
37548     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37549         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37550
37551     config.el = this.wrapper;
37552     //this.el = this.wrapper;
37553     
37554       if (config.container) {
37555         // ctor'ed from a Border/panel.grid
37556         
37557         
37558         this.wrapper.setStyle("overflow", "hidden");
37559         this.wrapper.addClass('roo-grid-container');
37560
37561     }
37562     
37563     
37564     if(config.toolbar){
37565         var tool_el = this.wrapper.createChild();    
37566         this.toolbar = Roo.factory(config.toolbar);
37567         var ti = [];
37568         if (config.toolbar.items) {
37569             ti = config.toolbar.items ;
37570             delete config.toolbar.items ;
37571         }
37572         
37573         var nitems = [];
37574         this.toolbar.render(tool_el);
37575         for(var i =0;i < ti.length;i++) {
37576           //  Roo.log(['add child', items[i]]);
37577             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37578         }
37579         this.toolbar.items = nitems;
37580         
37581         delete config.toolbar;
37582     }
37583     
37584     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37585     config.grid.scrollBody = true;;
37586     config.grid.monitorWindowResize = false; // turn off autosizing
37587     config.grid.autoHeight = false;
37588     config.grid.autoWidth = false;
37589     
37590     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37591     
37592     if (config.background) {
37593         // render grid on panel activation (if panel background)
37594         this.on('activate', function(gp) {
37595             if (!gp.grid.rendered) {
37596                 gp.grid.render(this.wrapper);
37597                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37598             }
37599         });
37600             
37601     } else {
37602         this.grid.render(this.wrapper);
37603         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37604
37605     }
37606     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37607     // ??? needed ??? config.el = this.wrapper;
37608     
37609     
37610     
37611   
37612     // xtype created footer. - not sure if will work as we normally have to render first..
37613     if (this.footer && !this.footer.el && this.footer.xtype) {
37614         
37615         var ctr = this.grid.getView().getFooterPanel(true);
37616         this.footer.dataSource = this.grid.dataSource;
37617         this.footer = Roo.factory(this.footer, Roo);
37618         this.footer.render(ctr);
37619         
37620     }
37621     
37622     
37623     
37624     
37625      
37626 };
37627
37628 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37629     getId : function(){
37630         return this.grid.id;
37631     },
37632     
37633     /**
37634      * Returns the grid for this panel
37635      * @return {Roo.bootstrap.Table} 
37636      */
37637     getGrid : function(){
37638         return this.grid;    
37639     },
37640     
37641     setSize : function(width, height){
37642         if(!this.ignoreResize(width, height)){
37643             var grid = this.grid;
37644             var size = this.adjustForComponents(width, height);
37645             var gridel = grid.getGridEl();
37646             gridel.setSize(size.width, size.height);
37647             /*
37648             var thd = grid.getGridEl().select('thead',true).first();
37649             var tbd = grid.getGridEl().select('tbody', true).first();
37650             if (tbd) {
37651                 tbd.setSize(width, height - thd.getHeight());
37652             }
37653             */
37654             grid.autoSize();
37655         }
37656     },
37657      
37658     
37659     
37660     beforeSlide : function(){
37661         this.grid.getView().scroller.clip();
37662     },
37663     
37664     afterSlide : function(){
37665         this.grid.getView().scroller.unclip();
37666     },
37667     
37668     destroy : function(){
37669         this.grid.destroy();
37670         delete this.grid;
37671         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37672     }
37673 });
37674
37675 /**
37676  * @class Roo.bootstrap.panel.Nest
37677  * @extends Roo.bootstrap.panel.Content
37678  * @constructor
37679  * Create a new Panel, that can contain a layout.Border.
37680  * 
37681  * 
37682  * @param {Roo.BorderLayout} layout The layout for this panel
37683  * @param {String/Object} config A string to set only the title or a config object
37684  */
37685 Roo.bootstrap.panel.Nest = function(config)
37686 {
37687     // construct with only one argument..
37688     /* FIXME - implement nicer consturctors
37689     if (layout.layout) {
37690         config = layout;
37691         layout = config.layout;
37692         delete config.layout;
37693     }
37694     if (layout.xtype && !layout.getEl) {
37695         // then layout needs constructing..
37696         layout = Roo.factory(layout, Roo);
37697     }
37698     */
37699     
37700     config.el =  config.layout.getEl();
37701     
37702     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37703     
37704     config.layout.monitorWindowResize = false; // turn off autosizing
37705     this.layout = config.layout;
37706     this.layout.getEl().addClass("roo-layout-nested-layout");
37707     
37708     
37709     
37710     
37711 };
37712
37713 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37714
37715     setSize : function(width, height){
37716         if(!this.ignoreResize(width, height)){
37717             var size = this.adjustForComponents(width, height);
37718             var el = this.layout.getEl();
37719             if (size.height < 1) {
37720                 el.setWidth(size.width);   
37721             } else {
37722                 el.setSize(size.width, size.height);
37723             }
37724             var touch = el.dom.offsetWidth;
37725             this.layout.layout();
37726             // ie requires a double layout on the first pass
37727             if(Roo.isIE && !this.initialized){
37728                 this.initialized = true;
37729                 this.layout.layout();
37730             }
37731         }
37732     },
37733     
37734     // activate all subpanels if not currently active..
37735     
37736     setActiveState : function(active){
37737         this.active = active;
37738         this.setActiveClass(active);
37739         
37740         if(!active){
37741             this.fireEvent("deactivate", this);
37742             return;
37743         }
37744         
37745         this.fireEvent("activate", this);
37746         // not sure if this should happen before or after..
37747         if (!this.layout) {
37748             return; // should not happen..
37749         }
37750         var reg = false;
37751         for (var r in this.layout.regions) {
37752             reg = this.layout.getRegion(r);
37753             if (reg.getActivePanel()) {
37754                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37755                 reg.setActivePanel(reg.getActivePanel());
37756                 continue;
37757             }
37758             if (!reg.panels.length) {
37759                 continue;
37760             }
37761             reg.showPanel(reg.getPanel(0));
37762         }
37763         
37764         
37765         
37766         
37767     },
37768     
37769     /**
37770      * Returns the nested BorderLayout for this panel
37771      * @return {Roo.BorderLayout} 
37772      */
37773     getLayout : function(){
37774         return this.layout;
37775     },
37776     
37777      /**
37778      * Adds a xtype elements to the layout of the nested panel
37779      * <pre><code>
37780
37781 panel.addxtype({
37782        xtype : 'ContentPanel',
37783        region: 'west',
37784        items: [ .... ]
37785    }
37786 );
37787
37788 panel.addxtype({
37789         xtype : 'NestedLayoutPanel',
37790         region: 'west',
37791         layout: {
37792            center: { },
37793            west: { }   
37794         },
37795         items : [ ... list of content panels or nested layout panels.. ]
37796    }
37797 );
37798 </code></pre>
37799      * @param {Object} cfg Xtype definition of item to add.
37800      */
37801     addxtype : function(cfg) {
37802         return this.layout.addxtype(cfg);
37803     
37804     }
37805 });        /*
37806  * Based on:
37807  * Ext JS Library 1.1.1
37808  * Copyright(c) 2006-2007, Ext JS, LLC.
37809  *
37810  * Originally Released Under LGPL - original licence link has changed is not relivant.
37811  *
37812  * Fork - LGPL
37813  * <script type="text/javascript">
37814  */
37815 /**
37816  * @class Roo.TabPanel
37817  * @extends Roo.util.Observable
37818  * A lightweight tab container.
37819  * <br><br>
37820  * Usage:
37821  * <pre><code>
37822 // basic tabs 1, built from existing content
37823 var tabs = new Roo.TabPanel("tabs1");
37824 tabs.addTab("script", "View Script");
37825 tabs.addTab("markup", "View Markup");
37826 tabs.activate("script");
37827
37828 // more advanced tabs, built from javascript
37829 var jtabs = new Roo.TabPanel("jtabs");
37830 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37831
37832 // set up the UpdateManager
37833 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37834 var updater = tab2.getUpdateManager();
37835 updater.setDefaultUrl("ajax1.htm");
37836 tab2.on('activate', updater.refresh, updater, true);
37837
37838 // Use setUrl for Ajax loading
37839 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37840 tab3.setUrl("ajax2.htm", null, true);
37841
37842 // Disabled tab
37843 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37844 tab4.disable();
37845
37846 jtabs.activate("jtabs-1");
37847  * </code></pre>
37848  * @constructor
37849  * Create a new TabPanel.
37850  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37851  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37852  */
37853 Roo.bootstrap.panel.Tabs = function(config){
37854     /**
37855     * The container element for this TabPanel.
37856     * @type Roo.Element
37857     */
37858     this.el = Roo.get(config.el);
37859     delete config.el;
37860     if(config){
37861         if(typeof config == "boolean"){
37862             this.tabPosition = config ? "bottom" : "top";
37863         }else{
37864             Roo.apply(this, config);
37865         }
37866     }
37867     
37868     if(this.tabPosition == "bottom"){
37869         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37870         this.el.addClass("roo-tabs-bottom");
37871     }
37872     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37873     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37874     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37875     if(Roo.isIE){
37876         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37877     }
37878     if(this.tabPosition != "bottom"){
37879         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37880          * @type Roo.Element
37881          */
37882         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37883         this.el.addClass("roo-tabs-top");
37884     }
37885     this.items = [];
37886
37887     this.bodyEl.setStyle("position", "relative");
37888
37889     this.active = null;
37890     this.activateDelegate = this.activate.createDelegate(this);
37891
37892     this.addEvents({
37893         /**
37894          * @event tabchange
37895          * Fires when the active tab changes
37896          * @param {Roo.TabPanel} this
37897          * @param {Roo.TabPanelItem} activePanel The new active tab
37898          */
37899         "tabchange": true,
37900         /**
37901          * @event beforetabchange
37902          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37903          * @param {Roo.TabPanel} this
37904          * @param {Object} e Set cancel to true on this object to cancel the tab change
37905          * @param {Roo.TabPanelItem} tab The tab being changed to
37906          */
37907         "beforetabchange" : true
37908     });
37909
37910     Roo.EventManager.onWindowResize(this.onResize, this);
37911     this.cpad = this.el.getPadding("lr");
37912     this.hiddenCount = 0;
37913
37914
37915     // toolbar on the tabbar support...
37916     if (this.toolbar) {
37917         alert("no toolbar support yet");
37918         this.toolbar  = false;
37919         /*
37920         var tcfg = this.toolbar;
37921         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37922         this.toolbar = new Roo.Toolbar(tcfg);
37923         if (Roo.isSafari) {
37924             var tbl = tcfg.container.child('table', true);
37925             tbl.setAttribute('width', '100%');
37926         }
37927         */
37928         
37929     }
37930    
37931
37932
37933     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37934 };
37935
37936 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37937     /*
37938      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37939      */
37940     tabPosition : "top",
37941     /*
37942      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37943      */
37944     currentTabWidth : 0,
37945     /*
37946      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37947      */
37948     minTabWidth : 40,
37949     /*
37950      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37951      */
37952     maxTabWidth : 250,
37953     /*
37954      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37955      */
37956     preferredTabWidth : 175,
37957     /*
37958      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37959      */
37960     resizeTabs : false,
37961     /*
37962      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37963      */
37964     monitorResize : true,
37965     /*
37966      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37967      */
37968     toolbar : false,
37969
37970     /**
37971      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37972      * @param {String} id The id of the div to use <b>or create</b>
37973      * @param {String} text The text for the tab
37974      * @param {String} content (optional) Content to put in the TabPanelItem body
37975      * @param {Boolean} closable (optional) True to create a close icon on the tab
37976      * @return {Roo.TabPanelItem} The created TabPanelItem
37977      */
37978     addTab : function(id, text, content, closable, tpl)
37979     {
37980         var item = new Roo.bootstrap.panel.TabItem({
37981             panel: this,
37982             id : id,
37983             text : text,
37984             closable : closable,
37985             tpl : tpl
37986         });
37987         this.addTabItem(item);
37988         if(content){
37989             item.setContent(content);
37990         }
37991         return item;
37992     },
37993
37994     /**
37995      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37996      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37997      * @return {Roo.TabPanelItem}
37998      */
37999     getTab : function(id){
38000         return this.items[id];
38001     },
38002
38003     /**
38004      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38005      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38006      */
38007     hideTab : function(id){
38008         var t = this.items[id];
38009         if(!t.isHidden()){
38010            t.setHidden(true);
38011            this.hiddenCount++;
38012            this.autoSizeTabs();
38013         }
38014     },
38015
38016     /**
38017      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38018      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38019      */
38020     unhideTab : function(id){
38021         var t = this.items[id];
38022         if(t.isHidden()){
38023            t.setHidden(false);
38024            this.hiddenCount--;
38025            this.autoSizeTabs();
38026         }
38027     },
38028
38029     /**
38030      * Adds an existing {@link Roo.TabPanelItem}.
38031      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38032      */
38033     addTabItem : function(item){
38034         this.items[item.id] = item;
38035         this.items.push(item);
38036       //  if(this.resizeTabs){
38037     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38038   //         this.autoSizeTabs();
38039 //        }else{
38040 //            item.autoSize();
38041        // }
38042     },
38043
38044     /**
38045      * Removes a {@link Roo.TabPanelItem}.
38046      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38047      */
38048     removeTab : function(id){
38049         var items = this.items;
38050         var tab = items[id];
38051         if(!tab) { return; }
38052         var index = items.indexOf(tab);
38053         if(this.active == tab && items.length > 1){
38054             var newTab = this.getNextAvailable(index);
38055             if(newTab) {
38056                 newTab.activate();
38057             }
38058         }
38059         this.stripEl.dom.removeChild(tab.pnode.dom);
38060         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38061             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38062         }
38063         items.splice(index, 1);
38064         delete this.items[tab.id];
38065         tab.fireEvent("close", tab);
38066         tab.purgeListeners();
38067         this.autoSizeTabs();
38068     },
38069
38070     getNextAvailable : function(start){
38071         var items = this.items;
38072         var index = start;
38073         // look for a next tab that will slide over to
38074         // replace the one being removed
38075         while(index < items.length){
38076             var item = items[++index];
38077             if(item && !item.isHidden()){
38078                 return item;
38079             }
38080         }
38081         // if one isn't found select the previous tab (on the left)
38082         index = start;
38083         while(index >= 0){
38084             var item = items[--index];
38085             if(item && !item.isHidden()){
38086                 return item;
38087             }
38088         }
38089         return null;
38090     },
38091
38092     /**
38093      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38094      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38095      */
38096     disableTab : function(id){
38097         var tab = this.items[id];
38098         if(tab && this.active != tab){
38099             tab.disable();
38100         }
38101     },
38102
38103     /**
38104      * Enables a {@link Roo.TabPanelItem} that is disabled.
38105      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38106      */
38107     enableTab : function(id){
38108         var tab = this.items[id];
38109         tab.enable();
38110     },
38111
38112     /**
38113      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38114      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38115      * @return {Roo.TabPanelItem} The TabPanelItem.
38116      */
38117     activate : function(id){
38118         var tab = this.items[id];
38119         if(!tab){
38120             return null;
38121         }
38122         if(tab == this.active || tab.disabled){
38123             return tab;
38124         }
38125         var e = {};
38126         this.fireEvent("beforetabchange", this, e, tab);
38127         if(e.cancel !== true && !tab.disabled){
38128             if(this.active){
38129                 this.active.hide();
38130             }
38131             this.active = this.items[id];
38132             this.active.show();
38133             this.fireEvent("tabchange", this, this.active);
38134         }
38135         return tab;
38136     },
38137
38138     /**
38139      * Gets the active {@link Roo.TabPanelItem}.
38140      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38141      */
38142     getActiveTab : function(){
38143         return this.active;
38144     },
38145
38146     /**
38147      * Updates the tab body element to fit the height of the container element
38148      * for overflow scrolling
38149      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38150      */
38151     syncHeight : function(targetHeight){
38152         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38153         var bm = this.bodyEl.getMargins();
38154         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38155         this.bodyEl.setHeight(newHeight);
38156         return newHeight;
38157     },
38158
38159     onResize : function(){
38160         if(this.monitorResize){
38161             this.autoSizeTabs();
38162         }
38163     },
38164
38165     /**
38166      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38167      */
38168     beginUpdate : function(){
38169         this.updating = true;
38170     },
38171
38172     /**
38173      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38174      */
38175     endUpdate : function(){
38176         this.updating = false;
38177         this.autoSizeTabs();
38178     },
38179
38180     /**
38181      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38182      */
38183     autoSizeTabs : function(){
38184         var count = this.items.length;
38185         var vcount = count - this.hiddenCount;
38186         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38187             return;
38188         }
38189         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38190         var availWidth = Math.floor(w / vcount);
38191         var b = this.stripBody;
38192         if(b.getWidth() > w){
38193             var tabs = this.items;
38194             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38195             if(availWidth < this.minTabWidth){
38196                 /*if(!this.sleft){    // incomplete scrolling code
38197                     this.createScrollButtons();
38198                 }
38199                 this.showScroll();
38200                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38201             }
38202         }else{
38203             if(this.currentTabWidth < this.preferredTabWidth){
38204                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38205             }
38206         }
38207     },
38208
38209     /**
38210      * Returns the number of tabs in this TabPanel.
38211      * @return {Number}
38212      */
38213      getCount : function(){
38214          return this.items.length;
38215      },
38216
38217     /**
38218      * Resizes all the tabs to the passed width
38219      * @param {Number} The new width
38220      */
38221     setTabWidth : function(width){
38222         this.currentTabWidth = width;
38223         for(var i = 0, len = this.items.length; i < len; i++) {
38224                 if(!this.items[i].isHidden()) {
38225                 this.items[i].setWidth(width);
38226             }
38227         }
38228     },
38229
38230     /**
38231      * Destroys this TabPanel
38232      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38233      */
38234     destroy : function(removeEl){
38235         Roo.EventManager.removeResizeListener(this.onResize, this);
38236         for(var i = 0, len = this.items.length; i < len; i++){
38237             this.items[i].purgeListeners();
38238         }
38239         if(removeEl === true){
38240             this.el.update("");
38241             this.el.remove();
38242         }
38243     },
38244     
38245     createStrip : function(container)
38246     {
38247         var strip = document.createElement("nav");
38248         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38249         container.appendChild(strip);
38250         return strip;
38251     },
38252     
38253     createStripList : function(strip)
38254     {
38255         // div wrapper for retard IE
38256         // returns the "tr" element.
38257         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38258         //'<div class="x-tabs-strip-wrap">'+
38259           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38260           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38261         return strip.firstChild; //.firstChild.firstChild.firstChild;
38262     },
38263     createBody : function(container)
38264     {
38265         var body = document.createElement("div");
38266         Roo.id(body, "tab-body");
38267         //Roo.fly(body).addClass("x-tabs-body");
38268         Roo.fly(body).addClass("tab-content");
38269         container.appendChild(body);
38270         return body;
38271     },
38272     createItemBody :function(bodyEl, id){
38273         var body = Roo.getDom(id);
38274         if(!body){
38275             body = document.createElement("div");
38276             body.id = id;
38277         }
38278         //Roo.fly(body).addClass("x-tabs-item-body");
38279         Roo.fly(body).addClass("tab-pane");
38280          bodyEl.insertBefore(body, bodyEl.firstChild);
38281         return body;
38282     },
38283     /** @private */
38284     createStripElements :  function(stripEl, text, closable, tpl)
38285     {
38286         var td = document.createElement("li"); // was td..
38287         
38288         
38289         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38290         
38291         
38292         stripEl.appendChild(td);
38293         /*if(closable){
38294             td.className = "x-tabs-closable";
38295             if(!this.closeTpl){
38296                 this.closeTpl = new Roo.Template(
38297                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38298                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38299                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38300                 );
38301             }
38302             var el = this.closeTpl.overwrite(td, {"text": text});
38303             var close = el.getElementsByTagName("div")[0];
38304             var inner = el.getElementsByTagName("em")[0];
38305             return {"el": el, "close": close, "inner": inner};
38306         } else {
38307         */
38308         // not sure what this is..
38309 //            if(!this.tabTpl){
38310                 //this.tabTpl = new Roo.Template(
38311                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38312                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38313                 //);
38314 //                this.tabTpl = new Roo.Template(
38315 //                   '<a href="#">' +
38316 //                   '<span unselectable="on"' +
38317 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38318 //                            ' >{text}</span></a>'
38319 //                );
38320 //                
38321 //            }
38322
38323
38324             var template = tpl || this.tabTpl || false;
38325             
38326             if(!template){
38327                 
38328                 template = new Roo.Template(
38329                    '<a href="#">' +
38330                    '<span unselectable="on"' +
38331                             (this.disableTooltips ? '' : ' title="{text}"') +
38332                             ' >{text}</span></a>'
38333                 );
38334             }
38335             
38336             switch (typeof(template)) {
38337                 case 'object' :
38338                     break;
38339                 case 'string' :
38340                     template = new Roo.Template(template);
38341                     break;
38342                 default :
38343                     break;
38344             }
38345             
38346             var el = template.overwrite(td, {"text": text});
38347             
38348             var inner = el.getElementsByTagName("span")[0];
38349             
38350             return {"el": el, "inner": inner};
38351             
38352     }
38353         
38354     
38355 });
38356
38357 /**
38358  * @class Roo.TabPanelItem
38359  * @extends Roo.util.Observable
38360  * Represents an individual item (tab plus body) in a TabPanel.
38361  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38362  * @param {String} id The id of this TabPanelItem
38363  * @param {String} text The text for the tab of this TabPanelItem
38364  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38365  */
38366 Roo.bootstrap.panel.TabItem = function(config){
38367     /**
38368      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38369      * @type Roo.TabPanel
38370      */
38371     this.tabPanel = config.panel;
38372     /**
38373      * The id for this TabPanelItem
38374      * @type String
38375      */
38376     this.id = config.id;
38377     /** @private */
38378     this.disabled = false;
38379     /** @private */
38380     this.text = config.text;
38381     /** @private */
38382     this.loaded = false;
38383     this.closable = config.closable;
38384
38385     /**
38386      * The body element for this TabPanelItem.
38387      * @type Roo.Element
38388      */
38389     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38390     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38391     this.bodyEl.setStyle("display", "block");
38392     this.bodyEl.setStyle("zoom", "1");
38393     //this.hideAction();
38394
38395     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38396     /** @private */
38397     this.el = Roo.get(els.el);
38398     this.inner = Roo.get(els.inner, true);
38399     this.textEl = Roo.get(this.el.dom.firstChild, true);
38400     this.pnode = Roo.get(els.el.parentNode, true);
38401 //    this.el.on("mousedown", this.onTabMouseDown, this);
38402     this.el.on("click", this.onTabClick, this);
38403     /** @private */
38404     if(config.closable){
38405         var c = Roo.get(els.close, true);
38406         c.dom.title = this.closeText;
38407         c.addClassOnOver("close-over");
38408         c.on("click", this.closeClick, this);
38409      }
38410
38411     this.addEvents({
38412          /**
38413          * @event activate
38414          * Fires when this tab becomes the active tab.
38415          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38416          * @param {Roo.TabPanelItem} this
38417          */
38418         "activate": true,
38419         /**
38420          * @event beforeclose
38421          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38422          * @param {Roo.TabPanelItem} this
38423          * @param {Object} e Set cancel to true on this object to cancel the close.
38424          */
38425         "beforeclose": true,
38426         /**
38427          * @event close
38428          * Fires when this tab is closed.
38429          * @param {Roo.TabPanelItem} this
38430          */
38431          "close": true,
38432         /**
38433          * @event deactivate
38434          * Fires when this tab is no longer the active tab.
38435          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38436          * @param {Roo.TabPanelItem} this
38437          */
38438          "deactivate" : true
38439     });
38440     this.hidden = false;
38441
38442     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38443 };
38444
38445 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38446            {
38447     purgeListeners : function(){
38448        Roo.util.Observable.prototype.purgeListeners.call(this);
38449        this.el.removeAllListeners();
38450     },
38451     /**
38452      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38453      */
38454     show : function(){
38455         this.pnode.addClass("active");
38456         this.showAction();
38457         if(Roo.isOpera){
38458             this.tabPanel.stripWrap.repaint();
38459         }
38460         this.fireEvent("activate", this.tabPanel, this);
38461     },
38462
38463     /**
38464      * Returns true if this tab is the active tab.
38465      * @return {Boolean}
38466      */
38467     isActive : function(){
38468         return this.tabPanel.getActiveTab() == this;
38469     },
38470
38471     /**
38472      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38473      */
38474     hide : function(){
38475         this.pnode.removeClass("active");
38476         this.hideAction();
38477         this.fireEvent("deactivate", this.tabPanel, this);
38478     },
38479
38480     hideAction : function(){
38481         this.bodyEl.hide();
38482         this.bodyEl.setStyle("position", "absolute");
38483         this.bodyEl.setLeft("-20000px");
38484         this.bodyEl.setTop("-20000px");
38485     },
38486
38487     showAction : function(){
38488         this.bodyEl.setStyle("position", "relative");
38489         this.bodyEl.setTop("");
38490         this.bodyEl.setLeft("");
38491         this.bodyEl.show();
38492     },
38493
38494     /**
38495      * Set the tooltip for the tab.
38496      * @param {String} tooltip The tab's tooltip
38497      */
38498     setTooltip : function(text){
38499         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38500             this.textEl.dom.qtip = text;
38501             this.textEl.dom.removeAttribute('title');
38502         }else{
38503             this.textEl.dom.title = text;
38504         }
38505     },
38506
38507     onTabClick : function(e){
38508         e.preventDefault();
38509         this.tabPanel.activate(this.id);
38510     },
38511
38512     onTabMouseDown : function(e){
38513         e.preventDefault();
38514         this.tabPanel.activate(this.id);
38515     },
38516 /*
38517     getWidth : function(){
38518         return this.inner.getWidth();
38519     },
38520
38521     setWidth : function(width){
38522         var iwidth = width - this.pnode.getPadding("lr");
38523         this.inner.setWidth(iwidth);
38524         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38525         this.pnode.setWidth(width);
38526     },
38527 */
38528     /**
38529      * Show or hide the tab
38530      * @param {Boolean} hidden True to hide or false to show.
38531      */
38532     setHidden : function(hidden){
38533         this.hidden = hidden;
38534         this.pnode.setStyle("display", hidden ? "none" : "");
38535     },
38536
38537     /**
38538      * Returns true if this tab is "hidden"
38539      * @return {Boolean}
38540      */
38541     isHidden : function(){
38542         return this.hidden;
38543     },
38544
38545     /**
38546      * Returns the text for this tab
38547      * @return {String}
38548      */
38549     getText : function(){
38550         return this.text;
38551     },
38552     /*
38553     autoSize : function(){
38554         //this.el.beginMeasure();
38555         this.textEl.setWidth(1);
38556         /*
38557          *  #2804 [new] Tabs in Roojs
38558          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38559          */
38560         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38561         //this.el.endMeasure();
38562     //},
38563
38564     /**
38565      * Sets the text for the tab (Note: this also sets the tooltip text)
38566      * @param {String} text The tab's text and tooltip
38567      */
38568     setText : function(text){
38569         this.text = text;
38570         this.textEl.update(text);
38571         this.setTooltip(text);
38572         //if(!this.tabPanel.resizeTabs){
38573         //    this.autoSize();
38574         //}
38575     },
38576     /**
38577      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38578      */
38579     activate : function(){
38580         this.tabPanel.activate(this.id);
38581     },
38582
38583     /**
38584      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38585      */
38586     disable : function(){
38587         if(this.tabPanel.active != this){
38588             this.disabled = true;
38589             this.pnode.addClass("disabled");
38590         }
38591     },
38592
38593     /**
38594      * Enables this TabPanelItem if it was previously disabled.
38595      */
38596     enable : function(){
38597         this.disabled = false;
38598         this.pnode.removeClass("disabled");
38599     },
38600
38601     /**
38602      * Sets the content for this TabPanelItem.
38603      * @param {String} content The content
38604      * @param {Boolean} loadScripts true to look for and load scripts
38605      */
38606     setContent : function(content, loadScripts){
38607         this.bodyEl.update(content, loadScripts);
38608     },
38609
38610     /**
38611      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38612      * @return {Roo.UpdateManager} The UpdateManager
38613      */
38614     getUpdateManager : function(){
38615         return this.bodyEl.getUpdateManager();
38616     },
38617
38618     /**
38619      * Set a URL to be used to load the content for this TabPanelItem.
38620      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38621      * @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)
38622      * @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)
38623      * @return {Roo.UpdateManager} The UpdateManager
38624      */
38625     setUrl : function(url, params, loadOnce){
38626         if(this.refreshDelegate){
38627             this.un('activate', this.refreshDelegate);
38628         }
38629         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38630         this.on("activate", this.refreshDelegate);
38631         return this.bodyEl.getUpdateManager();
38632     },
38633
38634     /** @private */
38635     _handleRefresh : function(url, params, loadOnce){
38636         if(!loadOnce || !this.loaded){
38637             var updater = this.bodyEl.getUpdateManager();
38638             updater.update(url, params, this._setLoaded.createDelegate(this));
38639         }
38640     },
38641
38642     /**
38643      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38644      *   Will fail silently if the setUrl method has not been called.
38645      *   This does not activate the panel, just updates its content.
38646      */
38647     refresh : function(){
38648         if(this.refreshDelegate){
38649            this.loaded = false;
38650            this.refreshDelegate();
38651         }
38652     },
38653
38654     /** @private */
38655     _setLoaded : function(){
38656         this.loaded = true;
38657     },
38658
38659     /** @private */
38660     closeClick : function(e){
38661         var o = {};
38662         e.stopEvent();
38663         this.fireEvent("beforeclose", this, o);
38664         if(o.cancel !== true){
38665             this.tabPanel.removeTab(this.id);
38666         }
38667     },
38668     /**
38669      * The text displayed in the tooltip for the close icon.
38670      * @type String
38671      */
38672     closeText : "Close this tab"
38673 });
38674 /**
38675 *    This script refer to:
38676 *    Title: International Telephone Input
38677 *    Author: Jack O'Connor
38678 *    Code version:  v12.1.12
38679 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38680 **/
38681
38682 Roo.bootstrap.PhoneInputData = function() {
38683     var d = [
38684       [
38685         "Afghanistan (‫افغانستان‬‎)",
38686         "af",
38687         "93"
38688       ],
38689       [
38690         "Albania (Shqipëri)",
38691         "al",
38692         "355"
38693       ],
38694       [
38695         "Algeria (‫الجزائر‬‎)",
38696         "dz",
38697         "213"
38698       ],
38699       [
38700         "American Samoa",
38701         "as",
38702         "1684"
38703       ],
38704       [
38705         "Andorra",
38706         "ad",
38707         "376"
38708       ],
38709       [
38710         "Angola",
38711         "ao",
38712         "244"
38713       ],
38714       [
38715         "Anguilla",
38716         "ai",
38717         "1264"
38718       ],
38719       [
38720         "Antigua and Barbuda",
38721         "ag",
38722         "1268"
38723       ],
38724       [
38725         "Argentina",
38726         "ar",
38727         "54"
38728       ],
38729       [
38730         "Armenia (Հայաստան)",
38731         "am",
38732         "374"
38733       ],
38734       [
38735         "Aruba",
38736         "aw",
38737         "297"
38738       ],
38739       [
38740         "Australia",
38741         "au",
38742         "61",
38743         0
38744       ],
38745       [
38746         "Austria (Österreich)",
38747         "at",
38748         "43"
38749       ],
38750       [
38751         "Azerbaijan (Azərbaycan)",
38752         "az",
38753         "994"
38754       ],
38755       [
38756         "Bahamas",
38757         "bs",
38758         "1242"
38759       ],
38760       [
38761         "Bahrain (‫البحرين‬‎)",
38762         "bh",
38763         "973"
38764       ],
38765       [
38766         "Bangladesh (বাংলাদেশ)",
38767         "bd",
38768         "880"
38769       ],
38770       [
38771         "Barbados",
38772         "bb",
38773         "1246"
38774       ],
38775       [
38776         "Belarus (Беларусь)",
38777         "by",
38778         "375"
38779       ],
38780       [
38781         "Belgium (België)",
38782         "be",
38783         "32"
38784       ],
38785       [
38786         "Belize",
38787         "bz",
38788         "501"
38789       ],
38790       [
38791         "Benin (Bénin)",
38792         "bj",
38793         "229"
38794       ],
38795       [
38796         "Bermuda",
38797         "bm",
38798         "1441"
38799       ],
38800       [
38801         "Bhutan (འབྲུག)",
38802         "bt",
38803         "975"
38804       ],
38805       [
38806         "Bolivia",
38807         "bo",
38808         "591"
38809       ],
38810       [
38811         "Bosnia and Herzegovina (Босна и Херцеговина)",
38812         "ba",
38813         "387"
38814       ],
38815       [
38816         "Botswana",
38817         "bw",
38818         "267"
38819       ],
38820       [
38821         "Brazil (Brasil)",
38822         "br",
38823         "55"
38824       ],
38825       [
38826         "British Indian Ocean Territory",
38827         "io",
38828         "246"
38829       ],
38830       [
38831         "British Virgin Islands",
38832         "vg",
38833         "1284"
38834       ],
38835       [
38836         "Brunei",
38837         "bn",
38838         "673"
38839       ],
38840       [
38841         "Bulgaria (България)",
38842         "bg",
38843         "359"
38844       ],
38845       [
38846         "Burkina Faso",
38847         "bf",
38848         "226"
38849       ],
38850       [
38851         "Burundi (Uburundi)",
38852         "bi",
38853         "257"
38854       ],
38855       [
38856         "Cambodia (កម្ពុជា)",
38857         "kh",
38858         "855"
38859       ],
38860       [
38861         "Cameroon (Cameroun)",
38862         "cm",
38863         "237"
38864       ],
38865       [
38866         "Canada",
38867         "ca",
38868         "1",
38869         1,
38870         ["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"]
38871       ],
38872       [
38873         "Cape Verde (Kabu Verdi)",
38874         "cv",
38875         "238"
38876       ],
38877       [
38878         "Caribbean Netherlands",
38879         "bq",
38880         "599",
38881         1
38882       ],
38883       [
38884         "Cayman Islands",
38885         "ky",
38886         "1345"
38887       ],
38888       [
38889         "Central African Republic (République centrafricaine)",
38890         "cf",
38891         "236"
38892       ],
38893       [
38894         "Chad (Tchad)",
38895         "td",
38896         "235"
38897       ],
38898       [
38899         "Chile",
38900         "cl",
38901         "56"
38902       ],
38903       [
38904         "China (中国)",
38905         "cn",
38906         "86"
38907       ],
38908       [
38909         "Christmas Island",
38910         "cx",
38911         "61",
38912         2
38913       ],
38914       [
38915         "Cocos (Keeling) Islands",
38916         "cc",
38917         "61",
38918         1
38919       ],
38920       [
38921         "Colombia",
38922         "co",
38923         "57"
38924       ],
38925       [
38926         "Comoros (‫جزر القمر‬‎)",
38927         "km",
38928         "269"
38929       ],
38930       [
38931         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38932         "cd",
38933         "243"
38934       ],
38935       [
38936         "Congo (Republic) (Congo-Brazzaville)",
38937         "cg",
38938         "242"
38939       ],
38940       [
38941         "Cook Islands",
38942         "ck",
38943         "682"
38944       ],
38945       [
38946         "Costa Rica",
38947         "cr",
38948         "506"
38949       ],
38950       [
38951         "Côte d’Ivoire",
38952         "ci",
38953         "225"
38954       ],
38955       [
38956         "Croatia (Hrvatska)",
38957         "hr",
38958         "385"
38959       ],
38960       [
38961         "Cuba",
38962         "cu",
38963         "53"
38964       ],
38965       [
38966         "Curaçao",
38967         "cw",
38968         "599",
38969         0
38970       ],
38971       [
38972         "Cyprus (Κύπρος)",
38973         "cy",
38974         "357"
38975       ],
38976       [
38977         "Czech Republic (Česká republika)",
38978         "cz",
38979         "420"
38980       ],
38981       [
38982         "Denmark (Danmark)",
38983         "dk",
38984         "45"
38985       ],
38986       [
38987         "Djibouti",
38988         "dj",
38989         "253"
38990       ],
38991       [
38992         "Dominica",
38993         "dm",
38994         "1767"
38995       ],
38996       [
38997         "Dominican Republic (República Dominicana)",
38998         "do",
38999         "1",
39000         2,
39001         ["809", "829", "849"]
39002       ],
39003       [
39004         "Ecuador",
39005         "ec",
39006         "593"
39007       ],
39008       [
39009         "Egypt (‫مصر‬‎)",
39010         "eg",
39011         "20"
39012       ],
39013       [
39014         "El Salvador",
39015         "sv",
39016         "503"
39017       ],
39018       [
39019         "Equatorial Guinea (Guinea Ecuatorial)",
39020         "gq",
39021         "240"
39022       ],
39023       [
39024         "Eritrea",
39025         "er",
39026         "291"
39027       ],
39028       [
39029         "Estonia (Eesti)",
39030         "ee",
39031         "372"
39032       ],
39033       [
39034         "Ethiopia",
39035         "et",
39036         "251"
39037       ],
39038       [
39039         "Falkland Islands (Islas Malvinas)",
39040         "fk",
39041         "500"
39042       ],
39043       [
39044         "Faroe Islands (Føroyar)",
39045         "fo",
39046         "298"
39047       ],
39048       [
39049         "Fiji",
39050         "fj",
39051         "679"
39052       ],
39053       [
39054         "Finland (Suomi)",
39055         "fi",
39056         "358",
39057         0
39058       ],
39059       [
39060         "France",
39061         "fr",
39062         "33"
39063       ],
39064       [
39065         "French Guiana (Guyane française)",
39066         "gf",
39067         "594"
39068       ],
39069       [
39070         "French Polynesia (Polynésie française)",
39071         "pf",
39072         "689"
39073       ],
39074       [
39075         "Gabon",
39076         "ga",
39077         "241"
39078       ],
39079       [
39080         "Gambia",
39081         "gm",
39082         "220"
39083       ],
39084       [
39085         "Georgia (საქართველო)",
39086         "ge",
39087         "995"
39088       ],
39089       [
39090         "Germany (Deutschland)",
39091         "de",
39092         "49"
39093       ],
39094       [
39095         "Ghana (Gaana)",
39096         "gh",
39097         "233"
39098       ],
39099       [
39100         "Gibraltar",
39101         "gi",
39102         "350"
39103       ],
39104       [
39105         "Greece (Ελλάδα)",
39106         "gr",
39107         "30"
39108       ],
39109       [
39110         "Greenland (Kalaallit Nunaat)",
39111         "gl",
39112         "299"
39113       ],
39114       [
39115         "Grenada",
39116         "gd",
39117         "1473"
39118       ],
39119       [
39120         "Guadeloupe",
39121         "gp",
39122         "590",
39123         0
39124       ],
39125       [
39126         "Guam",
39127         "gu",
39128         "1671"
39129       ],
39130       [
39131         "Guatemala",
39132         "gt",
39133         "502"
39134       ],
39135       [
39136         "Guernsey",
39137         "gg",
39138         "44",
39139         1
39140       ],
39141       [
39142         "Guinea (Guinée)",
39143         "gn",
39144         "224"
39145       ],
39146       [
39147         "Guinea-Bissau (Guiné Bissau)",
39148         "gw",
39149         "245"
39150       ],
39151       [
39152         "Guyana",
39153         "gy",
39154         "592"
39155       ],
39156       [
39157         "Haiti",
39158         "ht",
39159         "509"
39160       ],
39161       [
39162         "Honduras",
39163         "hn",
39164         "504"
39165       ],
39166       [
39167         "Hong Kong (香港)",
39168         "hk",
39169         "852"
39170       ],
39171       [
39172         "Hungary (Magyarország)",
39173         "hu",
39174         "36"
39175       ],
39176       [
39177         "Iceland (Ísland)",
39178         "is",
39179         "354"
39180       ],
39181       [
39182         "India (भारत)",
39183         "in",
39184         "91"
39185       ],
39186       [
39187         "Indonesia",
39188         "id",
39189         "62"
39190       ],
39191       [
39192         "Iran (‫ایران‬‎)",
39193         "ir",
39194         "98"
39195       ],
39196       [
39197         "Iraq (‫العراق‬‎)",
39198         "iq",
39199         "964"
39200       ],
39201       [
39202         "Ireland",
39203         "ie",
39204         "353"
39205       ],
39206       [
39207         "Isle of Man",
39208         "im",
39209         "44",
39210         2
39211       ],
39212       [
39213         "Israel (‫ישראל‬‎)",
39214         "il",
39215         "972"
39216       ],
39217       [
39218         "Italy (Italia)",
39219         "it",
39220         "39",
39221         0
39222       ],
39223       [
39224         "Jamaica",
39225         "jm",
39226         "1876"
39227       ],
39228       [
39229         "Japan (日本)",
39230         "jp",
39231         "81"
39232       ],
39233       [
39234         "Jersey",
39235         "je",
39236         "44",
39237         3
39238       ],
39239       [
39240         "Jordan (‫الأردن‬‎)",
39241         "jo",
39242         "962"
39243       ],
39244       [
39245         "Kazakhstan (Казахстан)",
39246         "kz",
39247         "7",
39248         1
39249       ],
39250       [
39251         "Kenya",
39252         "ke",
39253         "254"
39254       ],
39255       [
39256         "Kiribati",
39257         "ki",
39258         "686"
39259       ],
39260       [
39261         "Kosovo",
39262         "xk",
39263         "383"
39264       ],
39265       [
39266         "Kuwait (‫الكويت‬‎)",
39267         "kw",
39268         "965"
39269       ],
39270       [
39271         "Kyrgyzstan (Кыргызстан)",
39272         "kg",
39273         "996"
39274       ],
39275       [
39276         "Laos (ລາວ)",
39277         "la",
39278         "856"
39279       ],
39280       [
39281         "Latvia (Latvija)",
39282         "lv",
39283         "371"
39284       ],
39285       [
39286         "Lebanon (‫لبنان‬‎)",
39287         "lb",
39288         "961"
39289       ],
39290       [
39291         "Lesotho",
39292         "ls",
39293         "266"
39294       ],
39295       [
39296         "Liberia",
39297         "lr",
39298         "231"
39299       ],
39300       [
39301         "Libya (‫ليبيا‬‎)",
39302         "ly",
39303         "218"
39304       ],
39305       [
39306         "Liechtenstein",
39307         "li",
39308         "423"
39309       ],
39310       [
39311         "Lithuania (Lietuva)",
39312         "lt",
39313         "370"
39314       ],
39315       [
39316         "Luxembourg",
39317         "lu",
39318         "352"
39319       ],
39320       [
39321         "Macau (澳門)",
39322         "mo",
39323         "853"
39324       ],
39325       [
39326         "Macedonia (FYROM) (Македонија)",
39327         "mk",
39328         "389"
39329       ],
39330       [
39331         "Madagascar (Madagasikara)",
39332         "mg",
39333         "261"
39334       ],
39335       [
39336         "Malawi",
39337         "mw",
39338         "265"
39339       ],
39340       [
39341         "Malaysia",
39342         "my",
39343         "60"
39344       ],
39345       [
39346         "Maldives",
39347         "mv",
39348         "960"
39349       ],
39350       [
39351         "Mali",
39352         "ml",
39353         "223"
39354       ],
39355       [
39356         "Malta",
39357         "mt",
39358         "356"
39359       ],
39360       [
39361         "Marshall Islands",
39362         "mh",
39363         "692"
39364       ],
39365       [
39366         "Martinique",
39367         "mq",
39368         "596"
39369       ],
39370       [
39371         "Mauritania (‫موريتانيا‬‎)",
39372         "mr",
39373         "222"
39374       ],
39375       [
39376         "Mauritius (Moris)",
39377         "mu",
39378         "230"
39379       ],
39380       [
39381         "Mayotte",
39382         "yt",
39383         "262",
39384         1
39385       ],
39386       [
39387         "Mexico (México)",
39388         "mx",
39389         "52"
39390       ],
39391       [
39392         "Micronesia",
39393         "fm",
39394         "691"
39395       ],
39396       [
39397         "Moldova (Republica Moldova)",
39398         "md",
39399         "373"
39400       ],
39401       [
39402         "Monaco",
39403         "mc",
39404         "377"
39405       ],
39406       [
39407         "Mongolia (Монгол)",
39408         "mn",
39409         "976"
39410       ],
39411       [
39412         "Montenegro (Crna Gora)",
39413         "me",
39414         "382"
39415       ],
39416       [
39417         "Montserrat",
39418         "ms",
39419         "1664"
39420       ],
39421       [
39422         "Morocco (‫المغرب‬‎)",
39423         "ma",
39424         "212",
39425         0
39426       ],
39427       [
39428         "Mozambique (Moçambique)",
39429         "mz",
39430         "258"
39431       ],
39432       [
39433         "Myanmar (Burma) (မြန်မာ)",
39434         "mm",
39435         "95"
39436       ],
39437       [
39438         "Namibia (Namibië)",
39439         "na",
39440         "264"
39441       ],
39442       [
39443         "Nauru",
39444         "nr",
39445         "674"
39446       ],
39447       [
39448         "Nepal (नेपाल)",
39449         "np",
39450         "977"
39451       ],
39452       [
39453         "Netherlands (Nederland)",
39454         "nl",
39455         "31"
39456       ],
39457       [
39458         "New Caledonia (Nouvelle-Calédonie)",
39459         "nc",
39460         "687"
39461       ],
39462       [
39463         "New Zealand",
39464         "nz",
39465         "64"
39466       ],
39467       [
39468         "Nicaragua",
39469         "ni",
39470         "505"
39471       ],
39472       [
39473         "Niger (Nijar)",
39474         "ne",
39475         "227"
39476       ],
39477       [
39478         "Nigeria",
39479         "ng",
39480         "234"
39481       ],
39482       [
39483         "Niue",
39484         "nu",
39485         "683"
39486       ],
39487       [
39488         "Norfolk Island",
39489         "nf",
39490         "672"
39491       ],
39492       [
39493         "North Korea (조선 민주주의 인민 공화국)",
39494         "kp",
39495         "850"
39496       ],
39497       [
39498         "Northern Mariana Islands",
39499         "mp",
39500         "1670"
39501       ],
39502       [
39503         "Norway (Norge)",
39504         "no",
39505         "47",
39506         0
39507       ],
39508       [
39509         "Oman (‫عُمان‬‎)",
39510         "om",
39511         "968"
39512       ],
39513       [
39514         "Pakistan (‫پاکستان‬‎)",
39515         "pk",
39516         "92"
39517       ],
39518       [
39519         "Palau",
39520         "pw",
39521         "680"
39522       ],
39523       [
39524         "Palestine (‫فلسطين‬‎)",
39525         "ps",
39526         "970"
39527       ],
39528       [
39529         "Panama (Panamá)",
39530         "pa",
39531         "507"
39532       ],
39533       [
39534         "Papua New Guinea",
39535         "pg",
39536         "675"
39537       ],
39538       [
39539         "Paraguay",
39540         "py",
39541         "595"
39542       ],
39543       [
39544         "Peru (Perú)",
39545         "pe",
39546         "51"
39547       ],
39548       [
39549         "Philippines",
39550         "ph",
39551         "63"
39552       ],
39553       [
39554         "Poland (Polska)",
39555         "pl",
39556         "48"
39557       ],
39558       [
39559         "Portugal",
39560         "pt",
39561         "351"
39562       ],
39563       [
39564         "Puerto Rico",
39565         "pr",
39566         "1",
39567         3,
39568         ["787", "939"]
39569       ],
39570       [
39571         "Qatar (‫قطر‬‎)",
39572         "qa",
39573         "974"
39574       ],
39575       [
39576         "Réunion (La Réunion)",
39577         "re",
39578         "262",
39579         0
39580       ],
39581       [
39582         "Romania (România)",
39583         "ro",
39584         "40"
39585       ],
39586       [
39587         "Russia (Россия)",
39588         "ru",
39589         "7",
39590         0
39591       ],
39592       [
39593         "Rwanda",
39594         "rw",
39595         "250"
39596       ],
39597       [
39598         "Saint Barthélemy",
39599         "bl",
39600         "590",
39601         1
39602       ],
39603       [
39604         "Saint Helena",
39605         "sh",
39606         "290"
39607       ],
39608       [
39609         "Saint Kitts and Nevis",
39610         "kn",
39611         "1869"
39612       ],
39613       [
39614         "Saint Lucia",
39615         "lc",
39616         "1758"
39617       ],
39618       [
39619         "Saint Martin (Saint-Martin (partie française))",
39620         "mf",
39621         "590",
39622         2
39623       ],
39624       [
39625         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39626         "pm",
39627         "508"
39628       ],
39629       [
39630         "Saint Vincent and the Grenadines",
39631         "vc",
39632         "1784"
39633       ],
39634       [
39635         "Samoa",
39636         "ws",
39637         "685"
39638       ],
39639       [
39640         "San Marino",
39641         "sm",
39642         "378"
39643       ],
39644       [
39645         "São Tomé and Príncipe (São Tomé e Príncipe)",
39646         "st",
39647         "239"
39648       ],
39649       [
39650         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39651         "sa",
39652         "966"
39653       ],
39654       [
39655         "Senegal (Sénégal)",
39656         "sn",
39657         "221"
39658       ],
39659       [
39660         "Serbia (Србија)",
39661         "rs",
39662         "381"
39663       ],
39664       [
39665         "Seychelles",
39666         "sc",
39667         "248"
39668       ],
39669       [
39670         "Sierra Leone",
39671         "sl",
39672         "232"
39673       ],
39674       [
39675         "Singapore",
39676         "sg",
39677         "65"
39678       ],
39679       [
39680         "Sint Maarten",
39681         "sx",
39682         "1721"
39683       ],
39684       [
39685         "Slovakia (Slovensko)",
39686         "sk",
39687         "421"
39688       ],
39689       [
39690         "Slovenia (Slovenija)",
39691         "si",
39692         "386"
39693       ],
39694       [
39695         "Solomon Islands",
39696         "sb",
39697         "677"
39698       ],
39699       [
39700         "Somalia (Soomaaliya)",
39701         "so",
39702         "252"
39703       ],
39704       [
39705         "South Africa",
39706         "za",
39707         "27"
39708       ],
39709       [
39710         "South Korea (대한민국)",
39711         "kr",
39712         "82"
39713       ],
39714       [
39715         "South Sudan (‫جنوب السودان‬‎)",
39716         "ss",
39717         "211"
39718       ],
39719       [
39720         "Spain (España)",
39721         "es",
39722         "34"
39723       ],
39724       [
39725         "Sri Lanka (ශ්‍රී ලංකාව)",
39726         "lk",
39727         "94"
39728       ],
39729       [
39730         "Sudan (‫السودان‬‎)",
39731         "sd",
39732         "249"
39733       ],
39734       [
39735         "Suriname",
39736         "sr",
39737         "597"
39738       ],
39739       [
39740         "Svalbard and Jan Mayen",
39741         "sj",
39742         "47",
39743         1
39744       ],
39745       [
39746         "Swaziland",
39747         "sz",
39748         "268"
39749       ],
39750       [
39751         "Sweden (Sverige)",
39752         "se",
39753         "46"
39754       ],
39755       [
39756         "Switzerland (Schweiz)",
39757         "ch",
39758         "41"
39759       ],
39760       [
39761         "Syria (‫سوريا‬‎)",
39762         "sy",
39763         "963"
39764       ],
39765       [
39766         "Taiwan (台灣)",
39767         "tw",
39768         "886"
39769       ],
39770       [
39771         "Tajikistan",
39772         "tj",
39773         "992"
39774       ],
39775       [
39776         "Tanzania",
39777         "tz",
39778         "255"
39779       ],
39780       [
39781         "Thailand (ไทย)",
39782         "th",
39783         "66"
39784       ],
39785       [
39786         "Timor-Leste",
39787         "tl",
39788         "670"
39789       ],
39790       [
39791         "Togo",
39792         "tg",
39793         "228"
39794       ],
39795       [
39796         "Tokelau",
39797         "tk",
39798         "690"
39799       ],
39800       [
39801         "Tonga",
39802         "to",
39803         "676"
39804       ],
39805       [
39806         "Trinidad and Tobago",
39807         "tt",
39808         "1868"
39809       ],
39810       [
39811         "Tunisia (‫تونس‬‎)",
39812         "tn",
39813         "216"
39814       ],
39815       [
39816         "Turkey (Türkiye)",
39817         "tr",
39818         "90"
39819       ],
39820       [
39821         "Turkmenistan",
39822         "tm",
39823         "993"
39824       ],
39825       [
39826         "Turks and Caicos Islands",
39827         "tc",
39828         "1649"
39829       ],
39830       [
39831         "Tuvalu",
39832         "tv",
39833         "688"
39834       ],
39835       [
39836         "U.S. Virgin Islands",
39837         "vi",
39838         "1340"
39839       ],
39840       [
39841         "Uganda",
39842         "ug",
39843         "256"
39844       ],
39845       [
39846         "Ukraine (Україна)",
39847         "ua",
39848         "380"
39849       ],
39850       [
39851         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39852         "ae",
39853         "971"
39854       ],
39855       [
39856         "United Kingdom",
39857         "gb",
39858         "44",
39859         0
39860       ],
39861       [
39862         "United States",
39863         "us",
39864         "1",
39865         0
39866       ],
39867       [
39868         "Uruguay",
39869         "uy",
39870         "598"
39871       ],
39872       [
39873         "Uzbekistan (Oʻzbekiston)",
39874         "uz",
39875         "998"
39876       ],
39877       [
39878         "Vanuatu",
39879         "vu",
39880         "678"
39881       ],
39882       [
39883         "Vatican City (Città del Vaticano)",
39884         "va",
39885         "39",
39886         1
39887       ],
39888       [
39889         "Venezuela",
39890         "ve",
39891         "58"
39892       ],
39893       [
39894         "Vietnam (Việt Nam)",
39895         "vn",
39896         "84"
39897       ],
39898       [
39899         "Wallis and Futuna (Wallis-et-Futuna)",
39900         "wf",
39901         "681"
39902       ],
39903       [
39904         "Western Sahara (‫الصحراء الغربية‬‎)",
39905         "eh",
39906         "212",
39907         1
39908       ],
39909       [
39910         "Yemen (‫اليمن‬‎)",
39911         "ye",
39912         "967"
39913       ],
39914       [
39915         "Zambia",
39916         "zm",
39917         "260"
39918       ],
39919       [
39920         "Zimbabwe",
39921         "zw",
39922         "263"
39923       ],
39924       [
39925         "Åland Islands",
39926         "ax",
39927         "358",
39928         1
39929       ]
39930   ];
39931   
39932   return d;
39933 }/**
39934 *    This script refer to:
39935 *    Title: International Telephone Input
39936 *    Author: Jack O'Connor
39937 *    Code version:  v12.1.12
39938 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39939 **/
39940
39941 /**
39942  * @class Roo.bootstrap.PhoneInput
39943  * @extends Roo.bootstrap.TriggerField
39944  * An input with International dial-code selection
39945  
39946  * @cfg {String} defaultDialCode default '+852'
39947  * @cfg {Array} preferedCountries default []
39948   
39949  * @constructor
39950  * Create a new PhoneInput.
39951  * @param {Object} config Configuration options
39952  */
39953
39954 Roo.bootstrap.PhoneInput = function(config) {
39955     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39956 };
39957
39958 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39959         
39960         listWidth: undefined,
39961         
39962         selectedClass: 'active',
39963         
39964         invalidClass : "has-warning",
39965         
39966         validClass: 'has-success',
39967         
39968         allowed: '0123456789',
39969         
39970         max_length: 15,
39971         
39972         /**
39973          * @cfg {String} defaultDialCode The default dial code when initializing the input
39974          */
39975         defaultDialCode: '+852',
39976         
39977         /**
39978          * @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
39979          */
39980         preferedCountries: false,
39981         
39982         getAutoCreate : function()
39983         {
39984             var data = Roo.bootstrap.PhoneInputData();
39985             var align = this.labelAlign || this.parentLabelAlign();
39986             var id = Roo.id();
39987             
39988             this.allCountries = [];
39989             this.dialCodeMapping = [];
39990             
39991             for (var i = 0; i < data.length; i++) {
39992               var c = data[i];
39993               this.allCountries[i] = {
39994                 name: c[0],
39995                 iso2: c[1],
39996                 dialCode: c[2],
39997                 priority: c[3] || 0,
39998                 areaCodes: c[4] || null
39999               };
40000               this.dialCodeMapping[c[2]] = {
40001                   name: c[0],
40002                   iso2: c[1],
40003                   priority: c[3] || 0,
40004                   areaCodes: c[4] || null
40005               };
40006             }
40007             
40008             var cfg = {
40009                 cls: 'form-group',
40010                 cn: []
40011             };
40012             
40013             var input =  {
40014                 tag: 'input',
40015                 id : id,
40016                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40017                 maxlength: this.max_length,
40018                 cls : 'form-control tel-input',
40019                 autocomplete: 'new-password'
40020             };
40021             
40022             var hiddenInput = {
40023                 tag: 'input',
40024                 type: 'hidden',
40025                 cls: 'hidden-tel-input'
40026             };
40027             
40028             if (this.name) {
40029                 hiddenInput.name = this.name;
40030             }
40031             
40032             if (this.disabled) {
40033                 input.disabled = true;
40034             }
40035             
40036             var flag_container = {
40037                 tag: 'div',
40038                 cls: 'flag-box',
40039                 cn: [
40040                     {
40041                         tag: 'div',
40042                         cls: 'flag'
40043                     },
40044                     {
40045                         tag: 'div',
40046                         cls: 'caret'
40047                     }
40048                 ]
40049             };
40050             
40051             var box = {
40052                 tag: 'div',
40053                 cls: this.hasFeedback ? 'has-feedback' : '',
40054                 cn: [
40055                     hiddenInput,
40056                     input,
40057                     {
40058                         tag: 'input',
40059                         cls: 'dial-code-holder',
40060                         disabled: true
40061                     }
40062                 ]
40063             };
40064             
40065             var container = {
40066                 cls: 'roo-select2-container input-group',
40067                 cn: [
40068                     flag_container,
40069                     box
40070                 ]
40071             };
40072             
40073             if (this.fieldLabel.length) {
40074                 var indicator = {
40075                     tag: 'i',
40076                     tooltip: 'This field is required'
40077                 };
40078                 
40079                 var label = {
40080                     tag: 'label',
40081                     'for':  id,
40082                     cls: 'control-label',
40083                     cn: []
40084                 };
40085                 
40086                 var label_text = {
40087                     tag: 'span',
40088                     html: this.fieldLabel
40089                 };
40090                 
40091                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40092                 label.cn = [
40093                     indicator,
40094                     label_text
40095                 ];
40096                 
40097                 if(this.indicatorpos == 'right') {
40098                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40099                     label.cn = [
40100                         label_text,
40101                         indicator
40102                     ];
40103                 }
40104                 
40105                 if(align == 'left') {
40106                     container = {
40107                         tag: 'div',
40108                         cn: [
40109                             container
40110                         ]
40111                     };
40112                     
40113                     if(this.labelWidth > 12){
40114                         label.style = "width: " + this.labelWidth + 'px';
40115                     }
40116                     if(this.labelWidth < 13 && this.labelmd == 0){
40117                         this.labelmd = this.labelWidth;
40118                     }
40119                     if(this.labellg > 0){
40120                         label.cls += ' col-lg-' + this.labellg;
40121                         input.cls += ' col-lg-' + (12 - this.labellg);
40122                     }
40123                     if(this.labelmd > 0){
40124                         label.cls += ' col-md-' + this.labelmd;
40125                         container.cls += ' col-md-' + (12 - this.labelmd);
40126                     }
40127                     if(this.labelsm > 0){
40128                         label.cls += ' col-sm-' + this.labelsm;
40129                         container.cls += ' col-sm-' + (12 - this.labelsm);
40130                     }
40131                     if(this.labelxs > 0){
40132                         label.cls += ' col-xs-' + this.labelxs;
40133                         container.cls += ' col-xs-' + (12 - this.labelxs);
40134                     }
40135                 }
40136             }
40137             
40138             cfg.cn = [
40139                 label,
40140                 container
40141             ];
40142             
40143             var settings = this;
40144             
40145             ['xs','sm','md','lg'].map(function(size){
40146                 if (settings[size]) {
40147                     cfg.cls += ' col-' + size + '-' + settings[size];
40148                 }
40149             });
40150             
40151             this.store = new Roo.data.Store({
40152                 proxy : new Roo.data.MemoryProxy({}),
40153                 reader : new Roo.data.JsonReader({
40154                     fields : [
40155                         {
40156                             'name' : 'name',
40157                             'type' : 'string'
40158                         },
40159                         {
40160                             'name' : 'iso2',
40161                             'type' : 'string'
40162                         },
40163                         {
40164                             'name' : 'dialCode',
40165                             'type' : 'string'
40166                         },
40167                         {
40168                             'name' : 'priority',
40169                             'type' : 'string'
40170                         },
40171                         {
40172                             'name' : 'areaCodes',
40173                             'type' : 'string'
40174                         }
40175                     ]
40176                 })
40177             });
40178             
40179             if(!this.preferedCountries) {
40180                 this.preferedCountries = [
40181                     'hk',
40182                     'gb',
40183                     'us'
40184                 ];
40185             }
40186             
40187             var p = this.preferedCountries.reverse();
40188             
40189             if(p) {
40190                 for (var i = 0; i < p.length; i++) {
40191                     for (var j = 0; j < this.allCountries.length; j++) {
40192                         if(this.allCountries[j].iso2 == p[i]) {
40193                             var t = this.allCountries[j];
40194                             this.allCountries.splice(j,1);
40195                             this.allCountries.unshift(t);
40196                         }
40197                     } 
40198                 }
40199             }
40200             
40201             this.store.proxy.data = {
40202                 success: true,
40203                 data: this.allCountries
40204             };
40205             
40206             return cfg;
40207         },
40208         
40209         initEvents : function()
40210         {
40211             this.createList();
40212             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40213             
40214             this.indicator = this.indicatorEl();
40215             this.flag = this.flagEl();
40216             this.dialCodeHolder = this.dialCodeHolderEl();
40217             
40218             this.trigger = this.el.select('div.flag-box',true).first();
40219             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40220             
40221             var _this = this;
40222             
40223             (function(){
40224                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40225                 _this.list.setWidth(lw);
40226             }).defer(100);
40227             
40228             this.list.on('mouseover', this.onViewOver, this);
40229             this.list.on('mousemove', this.onViewMove, this);
40230             this.inputEl().on("keyup", this.onKeyUp, this);
40231             this.inputEl().on("keypress", this.onKeyPress, this);
40232             
40233             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40234
40235             this.view = new Roo.View(this.list, this.tpl, {
40236                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40237             });
40238             
40239             this.view.on('click', this.onViewClick, this);
40240             this.setValue(this.defaultDialCode);
40241         },
40242         
40243         onTriggerClick : function(e)
40244         {
40245             Roo.log('trigger click');
40246             if(this.disabled){
40247                 return;
40248             }
40249             
40250             if(this.isExpanded()){
40251                 this.collapse();
40252                 this.hasFocus = false;
40253             }else {
40254                 this.store.load({});
40255                 this.hasFocus = true;
40256                 this.expand();
40257             }
40258         },
40259         
40260         isExpanded : function()
40261         {
40262             return this.list.isVisible();
40263         },
40264         
40265         collapse : function()
40266         {
40267             if(!this.isExpanded()){
40268                 return;
40269             }
40270             this.list.hide();
40271             Roo.get(document).un('mousedown', this.collapseIf, this);
40272             Roo.get(document).un('mousewheel', this.collapseIf, this);
40273             this.fireEvent('collapse', this);
40274             this.validate();
40275         },
40276         
40277         expand : function()
40278         {
40279             Roo.log('expand');
40280
40281             if(this.isExpanded() || !this.hasFocus){
40282                 return;
40283             }
40284             
40285             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40286             this.list.setWidth(lw);
40287             
40288             this.list.show();
40289             this.restrictHeight();
40290             
40291             Roo.get(document).on('mousedown', this.collapseIf, this);
40292             Roo.get(document).on('mousewheel', this.collapseIf, this);
40293             
40294             this.fireEvent('expand', this);
40295         },
40296         
40297         restrictHeight : function()
40298         {
40299             this.list.alignTo(this.inputEl(), this.listAlign);
40300             this.list.alignTo(this.inputEl(), this.listAlign);
40301         },
40302         
40303         onViewOver : function(e, t)
40304         {
40305             if(this.inKeyMode){
40306                 return;
40307             }
40308             var item = this.view.findItemFromChild(t);
40309             
40310             if(item){
40311                 var index = this.view.indexOf(item);
40312                 this.select(index, false);
40313             }
40314         },
40315
40316         // private
40317         onViewClick : function(view, doFocus, el, e)
40318         {
40319             var index = this.view.getSelectedIndexes()[0];
40320             
40321             var r = this.store.getAt(index);
40322             
40323             if(r){
40324                 this.onSelect(r, index);
40325             }
40326             if(doFocus !== false && !this.blockFocus){
40327                 this.inputEl().focus();
40328             }
40329         },
40330         
40331         onViewMove : function(e, t)
40332         {
40333             this.inKeyMode = false;
40334         },
40335         
40336         select : function(index, scrollIntoView)
40337         {
40338             this.selectedIndex = index;
40339             this.view.select(index);
40340             if(scrollIntoView !== false){
40341                 var el = this.view.getNode(index);
40342                 if(el){
40343                     this.list.scrollChildIntoView(el, false);
40344                 }
40345             }
40346         },
40347         
40348         createList : function()
40349         {
40350             this.list = Roo.get(document.body).createChild({
40351                 tag: 'ul',
40352                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40353                 style: 'display:none'
40354             });
40355             
40356             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40357         },
40358         
40359         collapseIf : function(e)
40360         {
40361             var in_combo  = e.within(this.el);
40362             var in_list =  e.within(this.list);
40363             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40364             
40365             if (in_combo || in_list || is_list) {
40366                 return;
40367             }
40368             this.collapse();
40369         },
40370         
40371         onSelect : function(record, index)
40372         {
40373             if(this.fireEvent('beforeselect', this, record, index) !== false){
40374                 
40375                 this.setFlagClass(record.data.iso2);
40376                 this.setDialCode(record.data.dialCode);
40377                 this.hasFocus = false;
40378                 this.collapse();
40379                 this.fireEvent('select', this, record, index);
40380             }
40381         },
40382         
40383         flagEl : function()
40384         {
40385             var flag = this.el.select('div.flag',true).first();
40386             if(!flag){
40387                 return false;
40388             }
40389             return flag;
40390         },
40391         
40392         dialCodeHolderEl : function()
40393         {
40394             var d = this.el.select('input.dial-code-holder',true).first();
40395             if(!d){
40396                 return false;
40397             }
40398             return d;
40399         },
40400         
40401         setDialCode : function(v)
40402         {
40403             this.dialCodeHolder.dom.value = '+'+v;
40404         },
40405         
40406         setFlagClass : function(n)
40407         {
40408             this.flag.dom.className = 'flag '+n;
40409         },
40410         
40411         getValue : function()
40412         {
40413             var v = this.inputEl().getValue();
40414             if(this.dialCodeHolder) {
40415                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40416             }
40417             return v;
40418         },
40419         
40420         setValue : function(v)
40421         {
40422             var d = this.getDialCode(v);
40423             
40424             //invalid dial code
40425             if(v.length == 0 || !d || d.length == 0) {
40426                 if(this.rendered){
40427                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40428                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40429                 }
40430                 return;
40431             }
40432             
40433             //valid dial code
40434             this.setFlagClass(this.dialCodeMapping[d].iso2);
40435             this.setDialCode(d);
40436             this.inputEl().dom.value = v.replace('+'+d,'');
40437             this.hiddenEl().dom.value = this.getValue();
40438             
40439             this.validate();
40440         },
40441         
40442         getDialCode : function(v)
40443         {
40444             v = v ||  '';
40445             
40446             if (v.length == 0) {
40447                 return this.dialCodeHolder.dom.value;
40448             }
40449             
40450             var dialCode = "";
40451             if (v.charAt(0) != "+") {
40452                 return false;
40453             }
40454             var numericChars = "";
40455             for (var i = 1; i < v.length; i++) {
40456               var c = v.charAt(i);
40457               if (!isNaN(c)) {
40458                 numericChars += c;
40459                 if (this.dialCodeMapping[numericChars]) {
40460                   dialCode = v.substr(1, i);
40461                 }
40462                 if (numericChars.length == 4) {
40463                   break;
40464                 }
40465               }
40466             }
40467             return dialCode;
40468         },
40469         
40470         reset : function()
40471         {
40472             this.setValue(this.defaultDialCode);
40473             this.validate();
40474         },
40475         
40476         hiddenEl : function()
40477         {
40478             return this.el.select('input.hidden-tel-input',true).first();
40479         },
40480         
40481         // after setting val
40482         onKeyUp : function(e){
40483             this.setValue(this.getValue());
40484         },
40485         
40486         onKeyPress : function(e){
40487             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40488                 e.stopEvent();
40489             }
40490         }
40491         
40492 });
40493 /**
40494  * @class Roo.bootstrap.MoneyField
40495  * @extends Roo.bootstrap.ComboBox
40496  * Bootstrap MoneyField class
40497  * 
40498  * @constructor
40499  * Create a new MoneyField.
40500  * @param {Object} config Configuration options
40501  */
40502
40503 Roo.bootstrap.MoneyField = function(config) {
40504     
40505     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40506     
40507 };
40508
40509 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40510     
40511     /**
40512      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40513      */
40514     allowDecimals : true,
40515     /**
40516      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40517      */
40518     decimalSeparator : ".",
40519     /**
40520      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40521      */
40522     decimalPrecision : 0,
40523     /**
40524      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40525      */
40526     allowNegative : true,
40527     /**
40528      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40529      */
40530     allowZero: true,
40531     /**
40532      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40533      */
40534     minValue : Number.NEGATIVE_INFINITY,
40535     /**
40536      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40537      */
40538     maxValue : Number.MAX_VALUE,
40539     /**
40540      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40541      */
40542     minText : "The minimum value for this field is {0}",
40543     /**
40544      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40545      */
40546     maxText : "The maximum value for this field is {0}",
40547     /**
40548      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40549      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40550      */
40551     nanText : "{0} is not a valid number",
40552     /**
40553      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40554      */
40555     castInt : true,
40556     /**
40557      * @cfg {String} defaults currency of the MoneyField
40558      * value should be in lkey
40559      */
40560     defaultCurrency : false,
40561     /**
40562      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40563      */
40564     thousandsDelimiter : false,
40565     /**
40566      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40567      */
40568     max_length: false,
40569     
40570     inputlg : 9,
40571     inputmd : 9,
40572     inputsm : 9,
40573     inputxs : 6,
40574     
40575     store : false,
40576     
40577     getAutoCreate : function()
40578     {
40579         var align = this.labelAlign || this.parentLabelAlign();
40580         
40581         var id = Roo.id();
40582
40583         var cfg = {
40584             cls: 'form-group',
40585             cn: []
40586         };
40587
40588         var input =  {
40589             tag: 'input',
40590             id : id,
40591             cls : 'form-control roo-money-amount-input',
40592             autocomplete: 'new-password'
40593         };
40594         
40595         var hiddenInput = {
40596             tag: 'input',
40597             type: 'hidden',
40598             id: Roo.id(),
40599             cls: 'hidden-number-input'
40600         };
40601         
40602         if(this.max_length) {
40603             input.maxlength = this.max_length; 
40604         }
40605         
40606         if (this.name) {
40607             hiddenInput.name = this.name;
40608         }
40609
40610         if (this.disabled) {
40611             input.disabled = true;
40612         }
40613
40614         var clg = 12 - this.inputlg;
40615         var cmd = 12 - this.inputmd;
40616         var csm = 12 - this.inputsm;
40617         var cxs = 12 - this.inputxs;
40618         
40619         var container = {
40620             tag : 'div',
40621             cls : 'row roo-money-field',
40622             cn : [
40623                 {
40624                     tag : 'div',
40625                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40626                     cn : [
40627                         {
40628                             tag : 'div',
40629                             cls: 'roo-select2-container input-group',
40630                             cn: [
40631                                 {
40632                                     tag : 'input',
40633                                     cls : 'form-control roo-money-currency-input',
40634                                     autocomplete: 'new-password',
40635                                     readOnly : 1,
40636                                     name : this.currencyName
40637                                 },
40638                                 {
40639                                     tag :'span',
40640                                     cls : 'input-group-addon',
40641                                     cn : [
40642                                         {
40643                                             tag: 'span',
40644                                             cls: 'caret'
40645                                         }
40646                                     ]
40647                                 }
40648                             ]
40649                         }
40650                     ]
40651                 },
40652                 {
40653                     tag : 'div',
40654                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40655                     cn : [
40656                         {
40657                             tag: 'div',
40658                             cls: this.hasFeedback ? 'has-feedback' : '',
40659                             cn: [
40660                                 input
40661                             ]
40662                         }
40663                     ]
40664                 }
40665             ]
40666             
40667         };
40668         
40669         if (this.fieldLabel.length) {
40670             var indicator = {
40671                 tag: 'i',
40672                 tooltip: 'This field is required'
40673             };
40674
40675             var label = {
40676                 tag: 'label',
40677                 'for':  id,
40678                 cls: 'control-label',
40679                 cn: []
40680             };
40681
40682             var label_text = {
40683                 tag: 'span',
40684                 html: this.fieldLabel
40685             };
40686
40687             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40688             label.cn = [
40689                 indicator,
40690                 label_text
40691             ];
40692
40693             if(this.indicatorpos == 'right') {
40694                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40695                 label.cn = [
40696                     label_text,
40697                     indicator
40698                 ];
40699             }
40700
40701             if(align == 'left') {
40702                 container = {
40703                     tag: 'div',
40704                     cn: [
40705                         container
40706                     ]
40707                 };
40708
40709                 if(this.labelWidth > 12){
40710                     label.style = "width: " + this.labelWidth + 'px';
40711                 }
40712                 if(this.labelWidth < 13 && this.labelmd == 0){
40713                     this.labelmd = this.labelWidth;
40714                 }
40715                 if(this.labellg > 0){
40716                     label.cls += ' col-lg-' + this.labellg;
40717                     input.cls += ' col-lg-' + (12 - this.labellg);
40718                 }
40719                 if(this.labelmd > 0){
40720                     label.cls += ' col-md-' + this.labelmd;
40721                     container.cls += ' col-md-' + (12 - this.labelmd);
40722                 }
40723                 if(this.labelsm > 0){
40724                     label.cls += ' col-sm-' + this.labelsm;
40725                     container.cls += ' col-sm-' + (12 - this.labelsm);
40726                 }
40727                 if(this.labelxs > 0){
40728                     label.cls += ' col-xs-' + this.labelxs;
40729                     container.cls += ' col-xs-' + (12 - this.labelxs);
40730                 }
40731             }
40732         }
40733
40734         cfg.cn = [
40735             label,
40736             container,
40737             hiddenInput
40738         ];
40739         
40740         var settings = this;
40741
40742         ['xs','sm','md','lg'].map(function(size){
40743             if (settings[size]) {
40744                 cfg.cls += ' col-' + size + '-' + settings[size];
40745             }
40746         });
40747         
40748         return cfg;
40749     },
40750     
40751     initEvents : function()
40752     {
40753         this.indicator = this.indicatorEl();
40754         
40755         this.initCurrencyEvent();
40756         
40757         this.initNumberEvent();
40758     },
40759     
40760     initCurrencyEvent : function()
40761     {
40762         if (!this.store) {
40763             throw "can not find store for combo";
40764         }
40765         
40766         this.store = Roo.factory(this.store, Roo.data);
40767         this.store.parent = this;
40768         
40769         this.createList();
40770         
40771         this.triggerEl = this.el.select('.input-group-addon', true).first();
40772         
40773         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40774         
40775         var _this = this;
40776         
40777         (function(){
40778             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40779             _this.list.setWidth(lw);
40780         }).defer(100);
40781         
40782         this.list.on('mouseover', this.onViewOver, this);
40783         this.list.on('mousemove', this.onViewMove, this);
40784         this.list.on('scroll', this.onViewScroll, this);
40785         
40786         if(!this.tpl){
40787             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40788         }
40789         
40790         this.view = new Roo.View(this.list, this.tpl, {
40791             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40792         });
40793         
40794         this.view.on('click', this.onViewClick, this);
40795         
40796         this.store.on('beforeload', this.onBeforeLoad, this);
40797         this.store.on('load', this.onLoad, this);
40798         this.store.on('loadexception', this.onLoadException, this);
40799         
40800         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40801             "up" : function(e){
40802                 this.inKeyMode = true;
40803                 this.selectPrev();
40804             },
40805
40806             "down" : function(e){
40807                 if(!this.isExpanded()){
40808                     this.onTriggerClick();
40809                 }else{
40810                     this.inKeyMode = true;
40811                     this.selectNext();
40812                 }
40813             },
40814
40815             "enter" : function(e){
40816                 this.collapse();
40817                 
40818                 if(this.fireEvent("specialkey", this, e)){
40819                     this.onViewClick(false);
40820                 }
40821                 
40822                 return true;
40823             },
40824
40825             "esc" : function(e){
40826                 this.collapse();
40827             },
40828
40829             "tab" : function(e){
40830                 this.collapse();
40831                 
40832                 if(this.fireEvent("specialkey", this, e)){
40833                     this.onViewClick(false);
40834                 }
40835                 
40836                 return true;
40837             },
40838
40839             scope : this,
40840
40841             doRelay : function(foo, bar, hname){
40842                 if(hname == 'down' || this.scope.isExpanded()){
40843                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40844                 }
40845                 return true;
40846             },
40847
40848             forceKeyDown: true
40849         });
40850         
40851         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40852         
40853     },
40854     
40855     initNumberEvent : function(e)
40856     {
40857         this.inputEl().on("keydown" , this.fireKey,  this);
40858         this.inputEl().on("focus", this.onFocus,  this);
40859         this.inputEl().on("blur", this.onBlur,  this);
40860         
40861         this.inputEl().relayEvent('keyup', this);
40862         
40863         if(this.indicator){
40864             this.indicator.addClass('invisible');
40865         }
40866  
40867         this.originalValue = this.getValue();
40868         
40869         if(this.validationEvent == 'keyup'){
40870             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40871             this.inputEl().on('keyup', this.filterValidation, this);
40872         }
40873         else if(this.validationEvent !== false){
40874             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40875         }
40876         
40877         if(this.selectOnFocus){
40878             this.on("focus", this.preFocus, this);
40879             
40880         }
40881         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40882             this.inputEl().on("keypress", this.filterKeys, this);
40883         } else {
40884             this.inputEl().relayEvent('keypress', this);
40885         }
40886         
40887         var allowed = "0123456789";
40888         
40889         if(this.allowDecimals){
40890             allowed += this.decimalSeparator;
40891         }
40892         
40893         if(this.allowNegative){
40894             allowed += "-";
40895         }
40896         
40897         if(this.thousandsDelimiter) {
40898             allowed += ",";
40899         }
40900         
40901         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40902         
40903         var keyPress = function(e){
40904             
40905             var k = e.getKey();
40906             
40907             var c = e.getCharCode();
40908             
40909             if(
40910                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40911                     allowed.indexOf(String.fromCharCode(c)) === -1
40912             ){
40913                 e.stopEvent();
40914                 return;
40915             }
40916             
40917             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40918                 return;
40919             }
40920             
40921             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40922                 e.stopEvent();
40923             }
40924         };
40925         
40926         this.inputEl().on("keypress", keyPress, this);
40927         
40928     },
40929     
40930     onTriggerClick : function(e)
40931     {   
40932         if(this.disabled){
40933             return;
40934         }
40935         
40936         this.page = 0;
40937         this.loadNext = false;
40938         
40939         if(this.isExpanded()){
40940             this.collapse();
40941             return;
40942         }
40943         
40944         this.hasFocus = true;
40945         
40946         if(this.triggerAction == 'all') {
40947             this.doQuery(this.allQuery, true);
40948             return;
40949         }
40950         
40951         this.doQuery(this.getRawValue());
40952     },
40953     
40954     getCurrency : function()
40955     {   
40956         var v = this.currencyEl().getValue();
40957         
40958         return v;
40959     },
40960     
40961     restrictHeight : function()
40962     {
40963         this.list.alignTo(this.currencyEl(), this.listAlign);
40964         this.list.alignTo(this.currencyEl(), this.listAlign);
40965     },
40966     
40967     onViewClick : function(view, doFocus, el, e)
40968     {
40969         var index = this.view.getSelectedIndexes()[0];
40970         
40971         var r = this.store.getAt(index);
40972         
40973         if(r){
40974             this.onSelect(r, index);
40975         }
40976     },
40977     
40978     onSelect : function(record, index){
40979         
40980         if(this.fireEvent('beforeselect', this, record, index) !== false){
40981         
40982             this.setFromCurrencyData(index > -1 ? record.data : false);
40983             
40984             this.collapse();
40985             
40986             this.fireEvent('select', this, record, index);
40987         }
40988     },
40989     
40990     setFromCurrencyData : function(o)
40991     {
40992         var currency = '';
40993         
40994         this.lastCurrency = o;
40995         
40996         if (this.currencyField) {
40997             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40998         } else {
40999             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41000         }
41001         
41002         this.lastSelectionText = currency;
41003         
41004         //setting default currency
41005         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41006             this.setCurrency(this.defaultCurrency);
41007             return;
41008         }
41009         
41010         this.setCurrency(currency);
41011     },
41012     
41013     setFromData : function(o)
41014     {
41015         var c = {};
41016         
41017         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41018         
41019         this.setFromCurrencyData(c);
41020         
41021         var value = '';
41022         
41023         if (this.name) {
41024             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41025         } else {
41026             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41027         }
41028         
41029         this.setValue(value);
41030         
41031     },
41032     
41033     setCurrency : function(v)
41034     {   
41035         this.currencyValue = v;
41036         
41037         if(this.rendered){
41038             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41039             this.validate();
41040         }
41041     },
41042     
41043     setValue : function(v)
41044     {
41045         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41046         
41047         this.value = v;
41048         
41049         if(this.rendered){
41050             
41051             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41052             
41053             this.inputEl().dom.value = (v == '') ? '' :
41054                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41055             
41056             if(!this.allowZero && v === '0') {
41057                 this.hiddenEl().dom.value = '';
41058                 this.inputEl().dom.value = '';
41059             }
41060             
41061             this.validate();
41062         }
41063     },
41064     
41065     getRawValue : function()
41066     {
41067         var v = this.inputEl().getValue();
41068         
41069         return v;
41070     },
41071     
41072     getValue : function()
41073     {
41074         return this.fixPrecision(this.parseValue(this.getRawValue()));
41075     },
41076     
41077     parseValue : function(value)
41078     {
41079         if(this.thousandsDelimiter) {
41080             value += "";
41081             r = new RegExp(",", "g");
41082             value = value.replace(r, "");
41083         }
41084         
41085         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41086         return isNaN(value) ? '' : value;
41087         
41088     },
41089     
41090     fixPrecision : function(value)
41091     {
41092         if(this.thousandsDelimiter) {
41093             value += "";
41094             r = new RegExp(",", "g");
41095             value = value.replace(r, "");
41096         }
41097         
41098         var nan = isNaN(value);
41099         
41100         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41101             return nan ? '' : value;
41102         }
41103         return parseFloat(value).toFixed(this.decimalPrecision);
41104     },
41105     
41106     decimalPrecisionFcn : function(v)
41107     {
41108         return Math.floor(v);
41109     },
41110     
41111     validateValue : function(value)
41112     {
41113         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41114             return false;
41115         }
41116         
41117         var num = this.parseValue(value);
41118         
41119         if(isNaN(num)){
41120             this.markInvalid(String.format(this.nanText, value));
41121             return false;
41122         }
41123         
41124         if(num < this.minValue){
41125             this.markInvalid(String.format(this.minText, this.minValue));
41126             return false;
41127         }
41128         
41129         if(num > this.maxValue){
41130             this.markInvalid(String.format(this.maxText, this.maxValue));
41131             return false;
41132         }
41133         
41134         return true;
41135     },
41136     
41137     validate : function()
41138     {
41139         if(this.disabled || this.allowBlank){
41140             this.markValid();
41141             return true;
41142         }
41143         
41144         var currency = this.getCurrency();
41145         
41146         if(this.validateValue(this.getRawValue()) && currency.length){
41147             this.markValid();
41148             return true;
41149         }
41150         
41151         this.markInvalid();
41152         return false;
41153     },
41154     
41155     getName: function()
41156     {
41157         return this.name;
41158     },
41159     
41160     beforeBlur : function()
41161     {
41162         if(!this.castInt){
41163             return;
41164         }
41165         
41166         var v = this.parseValue(this.getRawValue());
41167         
41168         if(v || v == 0){
41169             this.setValue(v);
41170         }
41171     },
41172     
41173     onBlur : function()
41174     {
41175         this.beforeBlur();
41176         
41177         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41178             //this.el.removeClass(this.focusClass);
41179         }
41180         
41181         this.hasFocus = false;
41182         
41183         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41184             this.validate();
41185         }
41186         
41187         var v = this.getValue();
41188         
41189         if(String(v) !== String(this.startValue)){
41190             this.fireEvent('change', this, v, this.startValue);
41191         }
41192         
41193         this.fireEvent("blur", this);
41194     },
41195     
41196     inputEl : function()
41197     {
41198         return this.el.select('.roo-money-amount-input', true).first();
41199     },
41200     
41201     currencyEl : function()
41202     {
41203         return this.el.select('.roo-money-currency-input', true).first();
41204     },
41205     
41206     hiddenEl : function()
41207     {
41208         return this.el.select('input.hidden-number-input',true).first();
41209     }
41210     
41211 });