Roo/bootstrap/Navbar.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass('hidden');
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass('hidden');
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fs
593  * @cfg {String} badge text for badge
594  * @cfg {String} theme (default|glow)  
595  * @cfg {Boolean} inverse dark themed version
596  * @cfg {Boolean} toggle is it a slidy toggle button
597  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
598  * @cfg {String} ontext text for on slidy toggle state
599  * @cfg {String} offtext text for off slidy toggle state
600  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
601  * @cfg {Boolean} removeClass remove the standard class..
602  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
603  * 
604  * @constructor
605  * Create a new button
606  * @param {Object} config The config object
607  */
608
609
610 Roo.bootstrap.Button = function(config){
611     Roo.bootstrap.Button.superclass.constructor.call(this, config);
612     this.weightClass = ["btn-default btn-outline-secondary", 
613                        "btn-primary", 
614                        "btn-success", 
615                        "btn-info", 
616                        "btn-warning",
617                        "btn-danger",
618                        "btn-link"
619                       ],  
620     this.addEvents({
621         // raw events
622         /**
623          * @event click
624          * When a butotn is pressed
625          * @param {Roo.bootstrap.Button} btn
626          * @param {Roo.EventObject} e
627          */
628         "click" : true,
629          /**
630          * @event toggle
631          * After the button has been toggles
632          * @param {Roo.bootstrap.Button} btn
633          * @param {Roo.EventObject} e
634          * @param {boolean} pressed (also available as button.pressed)
635          */
636         "toggle" : true
637     });
638 };
639
640 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
641     html: false,
642     active: false,
643     weight: '',
644     badge_weight: '',
645     outline : false,
646     size: '',
647     tag: 'button',
648     href: '',
649     disabled: false,
650     isClose: false,
651     glyphicon: '',
652     fa: '',
653     badge: '',
654     theme: 'default',
655     inverse: false,
656     
657     toggle: false,
658     ontext: 'ON',
659     offtext: 'OFF',
660     defaulton: true,
661     preventDefault: true,
662     removeClass: false,
663     name: false,
664     target: false,
665      
666     pressed : null,
667      
668     
669     getAutoCreate : function(){
670         
671         var cfg = {
672             tag : 'button',
673             cls : 'roo-button',
674             html: ''
675         };
676         
677         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
678             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
679             this.tag = 'button';
680         } else {
681             cfg.tag = this.tag;
682         }
683         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
684         
685         if (this.toggle == true) {
686             cfg={
687                 tag: 'div',
688                 cls: 'slider-frame roo-button',
689                 cn: [
690                     {
691                         tag: 'span',
692                         'data-on-text':'ON',
693                         'data-off-text':'OFF',
694                         cls: 'slider-button',
695                         html: this.offtext
696                     }
697                 ]
698             };
699             
700             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
701                 cfg.cls += ' '+this.weight;
702             }
703             
704             return cfg;
705         }
706         
707         if (this.isClose) {
708             cfg.cls += ' close';
709             
710             cfg["aria-hidden"] = true;
711             
712             cfg.html = "&times;";
713             
714             return cfg;
715         }
716         
717          
718         if (this.theme==='default') {
719             cfg.cls = 'btn roo-button';
720             
721             //if (this.parentType != 'Navbar') {
722             this.weight = this.weight.length ?  this.weight : 'default';
723             //}
724             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
725                 
726                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
727                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
728                 cfg.cls += ' btn-' + outline + weight;
729                 if (this.weight == 'default') {
730                     // BC
731                     cfg.cls += ' btn-' + this.weight;
732                 }
733             }
734         } else if (this.theme==='glow') {
735             
736             cfg.tag = 'a';
737             cfg.cls = 'btn-glow roo-button';
738             
739             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
740                 
741                 cfg.cls += ' ' + this.weight;
742             }
743         }
744    
745         
746         if (this.inverse) {
747             this.cls += ' inverse';
748         }
749         
750         
751         if (this.active || this.pressed === true) {
752             cfg.cls += ' active';
753         }
754         
755         if (this.disabled) {
756             cfg.disabled = 'disabled';
757         }
758         
759         if (this.items) {
760             Roo.log('changing to ul' );
761             cfg.tag = 'ul';
762             this.glyphicon = 'caret';
763             if (Roo.bootstrap.version == 4) {
764                 this.fa = 'caret-down';
765             }
766             
767         }
768         
769         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
770          
771         //gsRoo.log(this.parentType);
772         if (this.parentType === 'Navbar' && !this.parent().bar) {
773             Roo.log('changing to li?');
774             
775             cfg.tag = 'li';
776             
777             cfg.cls = '';
778             cfg.cn =  [{
779                 tag : 'a',
780                 cls : 'roo-button',
781                 html : this.html,
782                 href : this.href || '#'
783             }];
784             if (this.menu) {
785                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
786                 cfg.cls += ' dropdown';
787             }   
788             
789             delete cfg.html;
790             
791         }
792         
793        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
794         
795         if (this.glyphicon) {
796             cfg.html = ' ' + cfg.html;
797             
798             cfg.cn = [
799                 {
800                     tag: 'span',
801                     cls: 'glyphicon glyphicon-' + this.glyphicon
802                 }
803             ];
804         }
805         if (this.fa) {
806             cfg.html = ' ' + cfg.html;
807             
808             cfg.cn = [
809                 {
810                     tag: 'i',
811                     cls: 'fa fas fa-' + this.fa
812                 }
813             ];
814         }
815         
816         if (this.badge) {
817             cfg.html += ' ';
818             
819             cfg.tag = 'a';
820             
821 //            cfg.cls='btn roo-button';
822             
823             cfg.href=this.href;
824             
825             var value = cfg.html;
826             
827             if(this.glyphicon){
828                 value = {
829                     tag: 'span',
830                     cls: 'glyphicon glyphicon-' + this.glyphicon,
831                     html: this.html
832                 };
833             }
834             if(this.fa){
835                 value = {
836                     tag: 'i',
837                     cls: 'fa fas fa-' + this.fa,
838                     html: this.html
839                 };
840             }
841             
842             var bw = this.badge_weight.length ? this.badge_weight :
843                 (this.weight.length ? this.weight : 'secondary');
844             bw = bw == 'default' ? 'secondary' : bw;
845             
846             cfg.cn = [
847                 value,
848                 {
849                     tag: 'span',
850                     cls: 'badge badge-' + bw,
851                     html: this.badge
852                 }
853             ];
854             
855             cfg.html='';
856         }
857         
858         if (this.menu) {
859             cfg.cls += ' dropdown';
860             cfg.html = typeof(cfg.html) != 'undefined' ?
861                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
862         }
863         
864         if (cfg.tag !== 'a' && this.href !== '') {
865             throw "Tag must be a to set href.";
866         } else if (this.href.length > 0) {
867             cfg.href = this.href;
868         }
869         
870         if(this.removeClass){
871             cfg.cls = '';
872         }
873         
874         if(this.target){
875             cfg.target = this.target;
876         }
877         
878         return cfg;
879     },
880     initEvents: function() {
881        // Roo.log('init events?');
882 //        Roo.log(this.el.dom);
883         // add the menu...
884         
885         if (typeof (this.menu) != 'undefined') {
886             this.menu.parentType = this.xtype;
887             this.menu.triggerEl = this.el;
888             this.addxtype(Roo.apply({}, this.menu));
889         }
890
891
892        if (this.el.hasClass('roo-button')) {
893             this.el.on('click', this.onClick, this);
894        } else {
895             this.el.select('.roo-button').on('click', this.onClick, this);
896        }
897        
898        if(this.removeClass){
899            this.el.on('click', this.onClick, this);
900        }
901        
902        this.el.enableDisplayMode();
903         
904     },
905     onClick : function(e)
906     {
907         if (this.disabled) {
908             return;
909         }
910         
911         Roo.log('button on click ');
912         if(this.preventDefault){
913             e.preventDefault();
914         }
915         
916         if (this.pressed === true || this.pressed === false) {
917             this.toggleActive(e);
918         }
919         
920         
921         this.fireEvent('click', this, e);
922     },
923     
924     /**
925      * Enables this button
926      */
927     enable : function()
928     {
929         this.disabled = false;
930         this.el.removeClass('disabled');
931     },
932     
933     /**
934      * Disable this button
935      */
936     disable : function()
937     {
938         this.disabled = true;
939         this.el.addClass('disabled');
940     },
941      /**
942      * sets the active state on/off, 
943      * @param {Boolean} state (optional) Force a particular state
944      */
945     setActive : function(v) {
946         
947         this.el[v ? 'addClass' : 'removeClass']('active');
948         this.pressed = v;
949     },
950      /**
951      * toggles the current active state 
952      */
953     toggleActive : function(e)
954     {
955         this.setActive(!this.pressed);
956         this.fireEvent('toggle', this, e, !this.pressed);
957     },
958      /**
959      * get the current active state
960      * @return {boolean} true if it's active
961      */
962     isActive : function()
963     {
964         return this.el.hasClass('active');
965     },
966     /**
967      * set the text of the first selected button
968      */
969     setText : function(str)
970     {
971         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
972     },
973     /**
974      * get the text of the first selected button
975      */
976     getText : function()
977     {
978         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
979     },
980     
981     setWeight : function(str)
982     {
983         this.el.removeClass(this.weightClass);
984         this.weight = str;
985         var outline = this.outline ? 'outline-' : '';
986         if (str == 'default') {
987             this.el.addClass('btn-default btn-outline-secondary');        
988             return;
989         }
990         this.el.addClass('btn-' + outline + str);        
991     }
992     
993     
994 });
995
996  /*
997  * - LGPL
998  *
999  * column
1000  * 
1001  */
1002
1003 /**
1004  * @class Roo.bootstrap.Column
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Column class
1007  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1008  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1009  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1010  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1011  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1012  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1013  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1014  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1015  *
1016  * 
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1019  * @cfg {String} fa (ban|check|...) font awesome icon
1020  * @cfg {Number} fasize (1|2|....) font awsome size
1021
1022  * @cfg {String} icon (info-sign|check|...) glyphicon name
1023
1024  * @cfg {String} html content of column.
1025  * 
1026  * @constructor
1027  * Create a new Column
1028  * @param {Object} config The config object
1029  */
1030
1031 Roo.bootstrap.Column = function(config){
1032     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1033 };
1034
1035 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1036     
1037     xs: false,
1038     sm: false,
1039     md: false,
1040     lg: false,
1041     xsoff: false,
1042     smoff: false,
1043     mdoff: false,
1044     lgoff: false,
1045     html: '',
1046     offset: 0,
1047     alert: false,
1048     fa: false,
1049     icon : false,
1050     hidden : false,
1051     fasize : 1,
1052     
1053     getAutoCreate : function(){
1054         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1055         
1056         cfg = {
1057             tag: 'div',
1058             cls: 'column'
1059         };
1060         
1061         var settings=this;
1062         ['xs','sm','md','lg'].map(function(size){
1063             //Roo.log( size + ':' + settings[size]);
1064             
1065             if (settings[size+'off'] !== false) {
1066                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1067             }
1068             
1069             if (settings[size] === false) {
1070                 return;
1071             }
1072             
1073             if (!settings[size]) { // 0 = hidden
1074                 cfg.cls += ' hidden-' + size;
1075                 return;
1076             }
1077             cfg.cls += ' col-' + size + '-' + settings[size];
1078             
1079         });
1080         
1081         if (this.hidden) {
1082             cfg.cls += ' hidden';
1083         }
1084         
1085         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1086             cfg.cls +=' alert alert-' + this.alert;
1087         }
1088         
1089         
1090         if (this.html.length) {
1091             cfg.html = this.html;
1092         }
1093         if (this.fa) {
1094             var fasize = '';
1095             if (this.fasize > 1) {
1096                 fasize = ' fa-' + this.fasize + 'x';
1097             }
1098             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1099             
1100             
1101         }
1102         if (this.icon) {
1103             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1104         }
1105         
1106         return cfg;
1107     }
1108    
1109 });
1110
1111  
1112
1113  /*
1114  * - LGPL
1115  *
1116  * page container.
1117  * 
1118  */
1119
1120
1121 /**
1122  * @class Roo.bootstrap.Container
1123  * @extends Roo.bootstrap.Component
1124  * Bootstrap Container class
1125  * @cfg {Boolean} jumbotron is it a jumbotron element
1126  * @cfg {String} html content of element
1127  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1128  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1129  * @cfg {String} header content of header (for panel)
1130  * @cfg {String} footer content of footer (for panel)
1131  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1132  * @cfg {String} tag (header|aside|section) type of HTML tag.
1133  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1134  * @cfg {String} fa font awesome icon
1135  * @cfg {String} icon (info-sign|check|...) glyphicon name
1136  * @cfg {Boolean} hidden (true|false) hide the element
1137  * @cfg {Boolean} expandable (true|false) default false
1138  * @cfg {Boolean} expanded (true|false) default true
1139  * @cfg {String} rheader contet on the right of header
1140  * @cfg {Boolean} clickable (true|false) default false
1141
1142  *     
1143  * @constructor
1144  * Create a new Container
1145  * @param {Object} config The config object
1146  */
1147
1148 Roo.bootstrap.Container = function(config){
1149     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1150     
1151     this.addEvents({
1152         // raw events
1153          /**
1154          * @event expand
1155          * After the panel has been expand
1156          * 
1157          * @param {Roo.bootstrap.Container} this
1158          */
1159         "expand" : true,
1160         /**
1161          * @event collapse
1162          * After the panel has been collapsed
1163          * 
1164          * @param {Roo.bootstrap.Container} this
1165          */
1166         "collapse" : true,
1167         /**
1168          * @event click
1169          * When a element is chick
1170          * @param {Roo.bootstrap.Container} this
1171          * @param {Roo.EventObject} e
1172          */
1173         "click" : true
1174     });
1175 };
1176
1177 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1178     
1179     jumbotron : false,
1180     well: '',
1181     panel : '',
1182     header: '',
1183     footer : '',
1184     sticky: '',
1185     tag : false,
1186     alert : false,
1187     fa: false,
1188     icon : false,
1189     expandable : false,
1190     rheader : '',
1191     expanded : true,
1192     clickable: false,
1193   
1194      
1195     getChildContainer : function() {
1196         
1197         if(!this.el){
1198             return false;
1199         }
1200         
1201         if (this.panel.length) {
1202             return this.el.select('.panel-body',true).first();
1203         }
1204         
1205         return this.el;
1206     },
1207     
1208     
1209     getAutoCreate : function(){
1210         
1211         var cfg = {
1212             tag : this.tag || 'div',
1213             html : '',
1214             cls : ''
1215         };
1216         if (this.jumbotron) {
1217             cfg.cls = 'jumbotron';
1218         }
1219         
1220         
1221         
1222         // - this is applied by the parent..
1223         //if (this.cls) {
1224         //    cfg.cls = this.cls + '';
1225         //}
1226         
1227         if (this.sticky.length) {
1228             
1229             var bd = Roo.get(document.body);
1230             if (!bd.hasClass('bootstrap-sticky')) {
1231                 bd.addClass('bootstrap-sticky');
1232                 Roo.select('html',true).setStyle('height', '100%');
1233             }
1234              
1235             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1236         }
1237         
1238         
1239         if (this.well.length) {
1240             switch (this.well) {
1241                 case 'lg':
1242                 case 'sm':
1243                     cfg.cls +=' well well-' +this.well;
1244                     break;
1245                 default:
1246                     cfg.cls +=' well';
1247                     break;
1248             }
1249         }
1250         
1251         if (this.hidden) {
1252             cfg.cls += ' hidden';
1253         }
1254         
1255         
1256         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1257             cfg.cls +=' alert alert-' + this.alert;
1258         }
1259         
1260         var body = cfg;
1261         
1262         if (this.panel.length) {
1263             cfg.cls += ' panel panel-' + this.panel;
1264             cfg.cn = [];
1265             if (this.header.length) {
1266                 
1267                 var h = [];
1268                 
1269                 if(this.expandable){
1270                     
1271                     cfg.cls = cfg.cls + ' expandable';
1272                     
1273                     h.push({
1274                         tag: 'i',
1275                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1276                     });
1277                     
1278                 }
1279                 
1280                 h.push(
1281                     {
1282                         tag: 'span',
1283                         cls : 'panel-title',
1284                         html : (this.expandable ? '&nbsp;' : '') + this.header
1285                     },
1286                     {
1287                         tag: 'span',
1288                         cls: 'panel-header-right',
1289                         html: this.rheader
1290                     }
1291                 );
1292                 
1293                 cfg.cn.push({
1294                     cls : 'panel-heading',
1295                     style : this.expandable ? 'cursor: pointer' : '',
1296                     cn : h
1297                 });
1298                 
1299             }
1300             
1301             body = false;
1302             cfg.cn.push({
1303                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1304                 html : this.html
1305             });
1306             
1307             
1308             if (this.footer.length) {
1309                 cfg.cn.push({
1310                     cls : 'panel-footer',
1311                     html : this.footer
1312                     
1313                 });
1314             }
1315             
1316         }
1317         
1318         if (body) {
1319             body.html = this.html || cfg.html;
1320             // prefix with the icons..
1321             if (this.fa) {
1322                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1323             }
1324             if (this.icon) {
1325                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1326             }
1327             
1328             
1329         }
1330         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1331             cfg.cls =  'container';
1332         }
1333         
1334         return cfg;
1335     },
1336     
1337     initEvents: function() 
1338     {
1339         if(this.expandable){
1340             var headerEl = this.headerEl();
1341         
1342             if(headerEl){
1343                 headerEl.on('click', this.onToggleClick, this);
1344             }
1345         }
1346         
1347         if(this.clickable){
1348             this.el.on('click', this.onClick, this);
1349         }
1350         
1351     },
1352     
1353     onToggleClick : function()
1354     {
1355         var headerEl = this.headerEl();
1356         
1357         if(!headerEl){
1358             return;
1359         }
1360         
1361         if(this.expanded){
1362             this.collapse();
1363             return;
1364         }
1365         
1366         this.expand();
1367     },
1368     
1369     expand : function()
1370     {
1371         if(this.fireEvent('expand', this)) {
1372             
1373             this.expanded = true;
1374             
1375             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1376             
1377             this.el.select('.panel-body',true).first().removeClass('hide');
1378             
1379             var toggleEl = this.toggleEl();
1380
1381             if(!toggleEl){
1382                 return;
1383             }
1384
1385             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1386         }
1387         
1388     },
1389     
1390     collapse : function()
1391     {
1392         if(this.fireEvent('collapse', this)) {
1393             
1394             this.expanded = false;
1395             
1396             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1397             this.el.select('.panel-body',true).first().addClass('hide');
1398         
1399             var toggleEl = this.toggleEl();
1400
1401             if(!toggleEl){
1402                 return;
1403             }
1404
1405             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1406         }
1407     },
1408     
1409     toggleEl : function()
1410     {
1411         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1412             return;
1413         }
1414         
1415         return this.el.select('.panel-heading .fa',true).first();
1416     },
1417     
1418     headerEl : function()
1419     {
1420         if(!this.el || !this.panel.length || !this.header.length){
1421             return;
1422         }
1423         
1424         return this.el.select('.panel-heading',true).first()
1425     },
1426     
1427     bodyEl : function()
1428     {
1429         if(!this.el || !this.panel.length){
1430             return;
1431         }
1432         
1433         return this.el.select('.panel-body',true).first()
1434     },
1435     
1436     titleEl : function()
1437     {
1438         if(!this.el || !this.panel.length || !this.header.length){
1439             return;
1440         }
1441         
1442         return this.el.select('.panel-title',true).first();
1443     },
1444     
1445     setTitle : function(v)
1446     {
1447         var titleEl = this.titleEl();
1448         
1449         if(!titleEl){
1450             return;
1451         }
1452         
1453         titleEl.dom.innerHTML = v;
1454     },
1455     
1456     getTitle : function()
1457     {
1458         
1459         var titleEl = this.titleEl();
1460         
1461         if(!titleEl){
1462             return '';
1463         }
1464         
1465         return titleEl.dom.innerHTML;
1466     },
1467     
1468     setRightTitle : function(v)
1469     {
1470         var t = this.el.select('.panel-header-right',true).first();
1471         
1472         if(!t){
1473             return;
1474         }
1475         
1476         t.dom.innerHTML = v;
1477     },
1478     
1479     onClick : function(e)
1480     {
1481         e.preventDefault();
1482         
1483         this.fireEvent('click', this, e);
1484     }
1485 });
1486
1487  /*
1488  * - LGPL
1489  *
1490  * image
1491  * 
1492  */
1493
1494
1495 /**
1496  * @class Roo.bootstrap.Img
1497  * @extends Roo.bootstrap.Component
1498  * Bootstrap Img class
1499  * @cfg {Boolean} imgResponsive false | true
1500  * @cfg {String} border rounded | circle | thumbnail
1501  * @cfg {String} src image source
1502  * @cfg {String} alt image alternative text
1503  * @cfg {String} href a tag href
1504  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1505  * @cfg {String} xsUrl xs image source
1506  * @cfg {String} smUrl sm image source
1507  * @cfg {String} mdUrl md image source
1508  * @cfg {String} lgUrl lg image source
1509  * 
1510  * @constructor
1511  * Create a new Input
1512  * @param {Object} config The config object
1513  */
1514
1515 Roo.bootstrap.Img = function(config){
1516     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1517     
1518     this.addEvents({
1519         // img events
1520         /**
1521          * @event click
1522          * The img click event for the img.
1523          * @param {Roo.EventObject} e
1524          */
1525         "click" : true
1526     });
1527 };
1528
1529 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1530     
1531     imgResponsive: true,
1532     border: '',
1533     src: 'about:blank',
1534     href: false,
1535     target: false,
1536     xsUrl: '',
1537     smUrl: '',
1538     mdUrl: '',
1539     lgUrl: '',
1540
1541     getAutoCreate : function()
1542     {   
1543         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1544             return this.createSingleImg();
1545         }
1546         
1547         var cfg = {
1548             tag: 'div',
1549             cls: 'roo-image-responsive-group',
1550             cn: []
1551         };
1552         var _this = this;
1553         
1554         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1555             
1556             if(!_this[size + 'Url']){
1557                 return;
1558             }
1559             
1560             var img = {
1561                 tag: 'img',
1562                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1563                 html: _this.html || cfg.html,
1564                 src: _this[size + 'Url']
1565             };
1566             
1567             img.cls += ' roo-image-responsive-' + size;
1568             
1569             var s = ['xs', 'sm', 'md', 'lg'];
1570             
1571             s.splice(s.indexOf(size), 1);
1572             
1573             Roo.each(s, function(ss){
1574                 img.cls += ' hidden-' + ss;
1575             });
1576             
1577             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1578                 cfg.cls += ' img-' + _this.border;
1579             }
1580             
1581             if(_this.alt){
1582                 cfg.alt = _this.alt;
1583             }
1584             
1585             if(_this.href){
1586                 var a = {
1587                     tag: 'a',
1588                     href: _this.href,
1589                     cn: [
1590                         img
1591                     ]
1592                 };
1593
1594                 if(this.target){
1595                     a.target = _this.target;
1596                 }
1597             }
1598             
1599             cfg.cn.push((_this.href) ? a : img);
1600             
1601         });
1602         
1603         return cfg;
1604     },
1605     
1606     createSingleImg : function()
1607     {
1608         var cfg = {
1609             tag: 'img',
1610             cls: (this.imgResponsive) ? 'img-responsive' : '',
1611             html : null,
1612             src : 'about:blank'  // just incase src get's set to undefined?!?
1613         };
1614         
1615         cfg.html = this.html || cfg.html;
1616         
1617         cfg.src = this.src || cfg.src;
1618         
1619         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1620             cfg.cls += ' img-' + this.border;
1621         }
1622         
1623         if(this.alt){
1624             cfg.alt = this.alt;
1625         }
1626         
1627         if(this.href){
1628             var a = {
1629                 tag: 'a',
1630                 href: this.href,
1631                 cn: [
1632                     cfg
1633                 ]
1634             };
1635             
1636             if(this.target){
1637                 a.target = this.target;
1638             }
1639             
1640         }
1641         
1642         return (this.href) ? a : cfg;
1643     },
1644     
1645     initEvents: function() 
1646     {
1647         if(!this.href){
1648             this.el.on('click', this.onClick, this);
1649         }
1650         
1651     },
1652     
1653     onClick : function(e)
1654     {
1655         Roo.log('img onclick');
1656         this.fireEvent('click', this, e);
1657     },
1658     /**
1659      * Sets the url of the image - used to update it
1660      * @param {String} url the url of the image
1661      */
1662     
1663     setSrc : function(url)
1664     {
1665         this.src =  url;
1666         
1667         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1668             this.el.dom.src =  url;
1669             return;
1670         }
1671         
1672         this.el.select('img', true).first().dom.src =  url;
1673     }
1674     
1675     
1676    
1677 });
1678
1679  /*
1680  * - LGPL
1681  *
1682  * image
1683  * 
1684  */
1685
1686
1687 /**
1688  * @class Roo.bootstrap.Link
1689  * @extends Roo.bootstrap.Component
1690  * Bootstrap Link Class
1691  * @cfg {String} alt image alternative text
1692  * @cfg {String} href a tag href
1693  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1694  * @cfg {String} html the content of the link.
1695  * @cfg {String} anchor name for the anchor link
1696  * @cfg {String} fa - favicon
1697
1698  * @cfg {Boolean} preventDefault (true | false) default false
1699
1700  * 
1701  * @constructor
1702  * Create a new Input
1703  * @param {Object} config The config object
1704  */
1705
1706 Roo.bootstrap.Link = function(config){
1707     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1708     
1709     this.addEvents({
1710         // img events
1711         /**
1712          * @event click
1713          * The img click event for the img.
1714          * @param {Roo.EventObject} e
1715          */
1716         "click" : true
1717     });
1718 };
1719
1720 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1721     
1722     href: false,
1723     target: false,
1724     preventDefault: false,
1725     anchor : false,
1726     alt : false,
1727     fa: false,
1728
1729
1730     getAutoCreate : function()
1731     {
1732         var html = this.html || '';
1733         
1734         if (this.fa !== false) {
1735             html = '<i class="fa fa-' + this.fa + '"></i>';
1736         }
1737         var cfg = {
1738             tag: 'a'
1739         };
1740         // anchor's do not require html/href...
1741         if (this.anchor === false) {
1742             cfg.html = html;
1743             cfg.href = this.href || '#';
1744         } else {
1745             cfg.name = this.anchor;
1746             if (this.html !== false || this.fa !== false) {
1747                 cfg.html = html;
1748             }
1749             if (this.href !== false) {
1750                 cfg.href = this.href;
1751             }
1752         }
1753         
1754         if(this.alt !== false){
1755             cfg.alt = this.alt;
1756         }
1757         
1758         
1759         if(this.target !== false) {
1760             cfg.target = this.target;
1761         }
1762         
1763         return cfg;
1764     },
1765     
1766     initEvents: function() {
1767         
1768         if(!this.href || this.preventDefault){
1769             this.el.on('click', this.onClick, this);
1770         }
1771     },
1772     
1773     onClick : function(e)
1774     {
1775         if(this.preventDefault){
1776             e.preventDefault();
1777         }
1778         //Roo.log('img onclick');
1779         this.fireEvent('click', this, e);
1780     }
1781    
1782 });
1783
1784  /*
1785  * - LGPL
1786  *
1787  * header
1788  * 
1789  */
1790
1791 /**
1792  * @class Roo.bootstrap.Header
1793  * @extends Roo.bootstrap.Component
1794  * Bootstrap Header class
1795  * @cfg {String} html content of header
1796  * @cfg {Number} level (1|2|3|4|5|6) default 1
1797  * 
1798  * @constructor
1799  * Create a new Header
1800  * @param {Object} config The config object
1801  */
1802
1803
1804 Roo.bootstrap.Header  = function(config){
1805     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1806 };
1807
1808 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1809     
1810     //href : false,
1811     html : false,
1812     level : 1,
1813     
1814     
1815     
1816     getAutoCreate : function(){
1817         
1818         
1819         
1820         var cfg = {
1821             tag: 'h' + (1 *this.level),
1822             html: this.html || ''
1823         } ;
1824         
1825         return cfg;
1826     }
1827    
1828 });
1829
1830  
1831
1832  /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842  
1843 /**
1844  * @class Roo.bootstrap.MenuMgr
1845  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1846  * @singleton
1847  */
1848 Roo.bootstrap.MenuMgr = function(){
1849    var menus, active, groups = {}, attached = false, lastShow = new Date();
1850
1851    // private - called when first menu is created
1852    function init(){
1853        menus = {};
1854        active = new Roo.util.MixedCollection();
1855        Roo.get(document).addKeyListener(27, function(){
1856            if(active.length > 0){
1857                hideAll();
1858            }
1859        });
1860    }
1861
1862    // private
1863    function hideAll(){
1864        if(active && active.length > 0){
1865            var c = active.clone();
1866            c.each(function(m){
1867                m.hide();
1868            });
1869        }
1870    }
1871
1872    // private
1873    function onHide(m){
1874        active.remove(m);
1875        if(active.length < 1){
1876            Roo.get(document).un("mouseup", onMouseDown);
1877             
1878            attached = false;
1879        }
1880    }
1881
1882    // private
1883    function onShow(m){
1884        var last = active.last();
1885        lastShow = new Date();
1886        active.add(m);
1887        if(!attached){
1888           Roo.get(document).on("mouseup", onMouseDown);
1889            
1890            attached = true;
1891        }
1892        if(m.parentMenu){
1893           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1894           m.parentMenu.activeChild = m;
1895        }else if(last && last.isVisible()){
1896           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1897        }
1898    }
1899
1900    // private
1901    function onBeforeHide(m){
1902        if(m.activeChild){
1903            m.activeChild.hide();
1904        }
1905        if(m.autoHideTimer){
1906            clearTimeout(m.autoHideTimer);
1907            delete m.autoHideTimer;
1908        }
1909    }
1910
1911    // private
1912    function onBeforeShow(m){
1913        var pm = m.parentMenu;
1914        if(!pm && !m.allowOtherMenus){
1915            hideAll();
1916        }else if(pm && pm.activeChild && active != m){
1917            pm.activeChild.hide();
1918        }
1919    }
1920
1921    // private this should really trigger on mouseup..
1922    function onMouseDown(e){
1923         Roo.log("on Mouse Up");
1924         
1925         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1926             Roo.log("MenuManager hideAll");
1927             hideAll();
1928             e.stopEvent();
1929         }
1930         
1931         
1932    }
1933
1934    // private
1935    function onBeforeCheck(mi, state){
1936        if(state){
1937            var g = groups[mi.group];
1938            for(var i = 0, l = g.length; i < l; i++){
1939                if(g[i] != mi){
1940                    g[i].setChecked(false);
1941                }
1942            }
1943        }
1944    }
1945
1946    return {
1947
1948        /**
1949         * Hides all menus that are currently visible
1950         */
1951        hideAll : function(){
1952             hideAll();  
1953        },
1954
1955        // private
1956        register : function(menu){
1957            if(!menus){
1958                init();
1959            }
1960            menus[menu.id] = menu;
1961            menu.on("beforehide", onBeforeHide);
1962            menu.on("hide", onHide);
1963            menu.on("beforeshow", onBeforeShow);
1964            menu.on("show", onShow);
1965            var g = menu.group;
1966            if(g && menu.events["checkchange"]){
1967                if(!groups[g]){
1968                    groups[g] = [];
1969                }
1970                groups[g].push(menu);
1971                menu.on("checkchange", onCheck);
1972            }
1973        },
1974
1975         /**
1976          * Returns a {@link Roo.menu.Menu} object
1977          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1978          * be used to generate and return a new Menu instance.
1979          */
1980        get : function(menu){
1981            if(typeof menu == "string"){ // menu id
1982                return menus[menu];
1983            }else if(menu.events){  // menu instance
1984                return menu;
1985            }
1986            /*else if(typeof menu.length == 'number'){ // array of menu items?
1987                return new Roo.bootstrap.Menu({items:menu});
1988            }else{ // otherwise, must be a config
1989                return new Roo.bootstrap.Menu(menu);
1990            }
1991            */
1992            return false;
1993        },
1994
1995        // private
1996        unregister : function(menu){
1997            delete menus[menu.id];
1998            menu.un("beforehide", onBeforeHide);
1999            menu.un("hide", onHide);
2000            menu.un("beforeshow", onBeforeShow);
2001            menu.un("show", onShow);
2002            var g = menu.group;
2003            if(g && menu.events["checkchange"]){
2004                groups[g].remove(menu);
2005                menu.un("checkchange", onCheck);
2006            }
2007        },
2008
2009        // private
2010        registerCheckable : function(menuItem){
2011            var g = menuItem.group;
2012            if(g){
2013                if(!groups[g]){
2014                    groups[g] = [];
2015                }
2016                groups[g].push(menuItem);
2017                menuItem.on("beforecheckchange", onBeforeCheck);
2018            }
2019        },
2020
2021        // private
2022        unregisterCheckable : function(menuItem){
2023            var g = menuItem.group;
2024            if(g){
2025                groups[g].remove(menuItem);
2026                menuItem.un("beforecheckchange", onBeforeCheck);
2027            }
2028        }
2029    };
2030 }();/*
2031  * - LGPL
2032  *
2033  * menu
2034  * 
2035  */
2036
2037 /**
2038  * @class Roo.bootstrap.Menu
2039  * @extends Roo.bootstrap.Component
2040  * Bootstrap Menu class - container for MenuItems
2041  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2042  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2043  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2044  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2045  * 
2046  * @constructor
2047  * Create a new Menu
2048  * @param {Object} config The config object
2049  */
2050
2051
2052 Roo.bootstrap.Menu = function(config){
2053     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2054     if (this.registerMenu && this.type != 'treeview')  {
2055         Roo.bootstrap.MenuMgr.register(this);
2056     }
2057     
2058     
2059     this.addEvents({
2060         /**
2061          * @event beforeshow
2062          * Fires before this menu is displayed
2063          * @param {Roo.menu.Menu} this
2064          */
2065         beforeshow : true,
2066         /**
2067          * @event beforehide
2068          * Fires before this menu is hidden
2069          * @param {Roo.menu.Menu} this
2070          */
2071         beforehide : true,
2072         /**
2073          * @event show
2074          * Fires after this menu is displayed
2075          * @param {Roo.menu.Menu} this
2076          */
2077         show : true,
2078         /**
2079          * @event hide
2080          * Fires after this menu is hidden
2081          * @param {Roo.menu.Menu} this
2082          */
2083         hide : true,
2084         /**
2085          * @event click
2086          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2087          * @param {Roo.menu.Menu} this
2088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2089          * @param {Roo.EventObject} e
2090          */
2091         click : true,
2092         /**
2093          * @event mouseover
2094          * Fires when the mouse is hovering over this menu
2095          * @param {Roo.menu.Menu} this
2096          * @param {Roo.EventObject} e
2097          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2098          */
2099         mouseover : true,
2100         /**
2101          * @event mouseout
2102          * Fires when the mouse exits this menu
2103          * @param {Roo.menu.Menu} this
2104          * @param {Roo.EventObject} e
2105          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2106          */
2107         mouseout : true,
2108         /**
2109          * @event itemclick
2110          * Fires when a menu item contained in this menu is clicked
2111          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2112          * @param {Roo.EventObject} e
2113          */
2114         itemclick: true
2115     });
2116     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2117 };
2118
2119 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2120     
2121    /// html : false,
2122     //align : '',
2123     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2124     type: false,
2125     /**
2126      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2127      */
2128     registerMenu : true,
2129     
2130     menuItems :false, // stores the menu items..
2131     
2132     hidden:true,
2133         
2134     parentMenu : false,
2135     
2136     stopEvent : true,
2137     
2138     isLink : false,
2139     
2140     getChildContainer : function() {
2141         return this.el;  
2142     },
2143     
2144     getAutoCreate : function(){
2145          
2146         //if (['right'].indexOf(this.align)!==-1) {
2147         //    cfg.cn[1].cls += ' pull-right'
2148         //}
2149         
2150         
2151         var cfg = {
2152             tag : 'ul',
2153             cls : 'dropdown-menu' ,
2154             style : 'z-index:1000'
2155             
2156         };
2157         
2158         if (this.type === 'submenu') {
2159             cfg.cls = 'submenu active';
2160         }
2161         if (this.type === 'treeview') {
2162             cfg.cls = 'treeview-menu';
2163         }
2164         
2165         return cfg;
2166     },
2167     initEvents : function() {
2168         
2169        // Roo.log("ADD event");
2170        // Roo.log(this.triggerEl.dom);
2171         
2172         this.triggerEl.on('click', this.onTriggerClick, this);
2173         
2174         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2175         
2176         
2177         if (this.triggerEl.hasClass('nav-item')) {
2178             // dropdown toggle on the 'a' in BS4?
2179             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2180         } else {
2181             this.triggerEl.addClass('dropdown-toggle');
2182         }
2183         if (Roo.isTouch) {
2184             this.el.on('touchstart'  , this.onTouch, this);
2185         }
2186         this.el.on('click' , this.onClick, this);
2187
2188         this.el.on("mouseover", this.onMouseOver, this);
2189         this.el.on("mouseout", this.onMouseOut, this);
2190         
2191     },
2192     
2193     findTargetItem : function(e)
2194     {
2195         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2196         if(!t){
2197             return false;
2198         }
2199         //Roo.log(t);         Roo.log(t.id);
2200         if(t && t.id){
2201             //Roo.log(this.menuitems);
2202             return this.menuitems.get(t.id);
2203             
2204             //return this.items.get(t.menuItemId);
2205         }
2206         
2207         return false;
2208     },
2209     
2210     onTouch : function(e) 
2211     {
2212         Roo.log("menu.onTouch");
2213         //e.stopEvent(); this make the user popdown broken
2214         this.onClick(e);
2215     },
2216     
2217     onClick : function(e)
2218     {
2219         Roo.log("menu.onClick");
2220         
2221         var t = this.findTargetItem(e);
2222         if(!t || t.isContainer){
2223             return;
2224         }
2225         Roo.log(e);
2226         /*
2227         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2228             if(t == this.activeItem && t.shouldDeactivate(e)){
2229                 this.activeItem.deactivate();
2230                 delete this.activeItem;
2231                 return;
2232             }
2233             if(t.canActivate){
2234                 this.setActiveItem(t, true);
2235             }
2236             return;
2237             
2238             
2239         }
2240         */
2241        
2242         Roo.log('pass click event');
2243         
2244         t.onClick(e);
2245         
2246         this.fireEvent("click", this, t, e);
2247         
2248         var _this = this;
2249         
2250         if(!t.href.length || t.href == '#'){
2251             (function() { _this.hide(); }).defer(100);
2252         }
2253         
2254     },
2255     
2256     onMouseOver : function(e){
2257         var t  = this.findTargetItem(e);
2258         //Roo.log(t);
2259         //if(t){
2260         //    if(t.canActivate && !t.disabled){
2261         //        this.setActiveItem(t, true);
2262         //    }
2263         //}
2264         
2265         this.fireEvent("mouseover", this, e, t);
2266     },
2267     isVisible : function(){
2268         return !this.hidden;
2269     },
2270      onMouseOut : function(e){
2271         var t  = this.findTargetItem(e);
2272         
2273         //if(t ){
2274         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2275         //        this.activeItem.deactivate();
2276         //        delete this.activeItem;
2277         //    }
2278         //}
2279         this.fireEvent("mouseout", this, e, t);
2280     },
2281     
2282     
2283     /**
2284      * Displays this menu relative to another element
2285      * @param {String/HTMLElement/Roo.Element} element The element to align to
2286      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2287      * the element (defaults to this.defaultAlign)
2288      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2289      */
2290     show : function(el, pos, parentMenu){
2291         this.parentMenu = parentMenu;
2292         if(!this.el){
2293             this.render();
2294         }
2295         this.fireEvent("beforeshow", this);
2296         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2297     },
2298      /**
2299      * Displays this menu at a specific xy position
2300      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2301      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2302      */
2303     showAt : function(xy, parentMenu, /* private: */_e){
2304         this.parentMenu = parentMenu;
2305         if(!this.el){
2306             this.render();
2307         }
2308         if(_e !== false){
2309             this.fireEvent("beforeshow", this);
2310             //xy = this.el.adjustForConstraints(xy);
2311         }
2312         
2313         //this.el.show();
2314         this.hideMenuItems();
2315         this.hidden = false;
2316         this.triggerEl.addClass('open');
2317         this.el.addClass('show');
2318         
2319         // reassign x when hitting right
2320         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2321             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2322         }
2323         
2324         // reassign y when hitting bottom
2325         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2326             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2327         }
2328         
2329         // but the list may align on trigger left or trigger top... should it be a properity?
2330         
2331         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2332             this.el.setXY(xy);
2333         }
2334         
2335         this.focus();
2336         this.fireEvent("show", this);
2337     },
2338     
2339     focus : function(){
2340         return;
2341         if(!this.hidden){
2342             this.doFocus.defer(50, this);
2343         }
2344     },
2345
2346     doFocus : function(){
2347         if(!this.hidden){
2348             this.focusEl.focus();
2349         }
2350     },
2351
2352     /**
2353      * Hides this menu and optionally all parent menus
2354      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2355      */
2356     hide : function(deep)
2357     {
2358         
2359         this.hideMenuItems();
2360         if(this.el && this.isVisible()){
2361             this.fireEvent("beforehide", this);
2362             if(this.activeItem){
2363                 this.activeItem.deactivate();
2364                 this.activeItem = null;
2365             }
2366             this.triggerEl.removeClass('open');;
2367             this.el.removeClass('show');
2368             this.hidden = true;
2369             this.fireEvent("hide", this);
2370         }
2371         if(deep === true && this.parentMenu){
2372             this.parentMenu.hide(true);
2373         }
2374     },
2375     
2376     onTriggerClick : function(e)
2377     {
2378         Roo.log('trigger click');
2379         
2380         var target = e.getTarget();
2381         
2382         Roo.log(target.nodeName.toLowerCase());
2383         
2384         if(target.nodeName.toLowerCase() === 'i'){
2385             e.preventDefault();
2386         }
2387         
2388     },
2389     
2390     onTriggerPress  : function(e)
2391     {
2392         Roo.log('trigger press');
2393         //Roo.log(e.getTarget());
2394        // Roo.log(this.triggerEl.dom);
2395        
2396         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2397         var pel = Roo.get(e.getTarget());
2398         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2399             Roo.log('is treeview or dropdown?');
2400             return;
2401         }
2402         
2403         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2404             return;
2405         }
2406         
2407         if (this.isVisible()) {
2408             Roo.log('hide');
2409             this.hide();
2410         } else {
2411             Roo.log('show');
2412             this.show(this.triggerEl, false, false);
2413         }
2414         
2415         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2416             e.stopEvent();
2417         }
2418         
2419     },
2420        
2421     
2422     hideMenuItems : function()
2423     {
2424         Roo.log("hide Menu Items");
2425         if (!this.el) { 
2426             return;
2427         }
2428         //$(backdrop).remove()
2429         this.el.select('.open',true).each(function(aa) {
2430             
2431             aa.removeClass('open');
2432           //var parent = getParent($(this))
2433           //var relatedTarget = { relatedTarget: this }
2434           
2435            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2436           //if (e.isDefaultPrevented()) return
2437            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2438         });
2439     },
2440     addxtypeChild : function (tree, cntr) {
2441         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2442           
2443         this.menuitems.add(comp);
2444         return comp;
2445
2446     },
2447     getEl : function()
2448     {
2449         Roo.log(this.el);
2450         return this.el;
2451     },
2452     
2453     clear : function()
2454     {
2455         this.getEl().dom.innerHTML = '';
2456         this.menuitems.clear();
2457     }
2458 });
2459
2460  
2461  /*
2462  * - LGPL
2463  *
2464  * menu item
2465  * 
2466  */
2467
2468
2469 /**
2470  * @class Roo.bootstrap.MenuItem
2471  * @extends Roo.bootstrap.Component
2472  * Bootstrap MenuItem class
2473  * @cfg {String} html the menu label
2474  * @cfg {String} href the link
2475  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2476  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2477  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2478  * @cfg {String} fa favicon to show on left of menu item.
2479  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2480  * 
2481  * 
2482  * @constructor
2483  * Create a new MenuItem
2484  * @param {Object} config The config object
2485  */
2486
2487
2488 Roo.bootstrap.MenuItem = function(config){
2489     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2490     this.addEvents({
2491         // raw events
2492         /**
2493          * @event click
2494          * The raw click event for the entire grid.
2495          * @param {Roo.bootstrap.MenuItem} this
2496          * @param {Roo.EventObject} e
2497          */
2498         "click" : true
2499     });
2500 };
2501
2502 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2503     
2504     href : false,
2505     html : false,
2506     preventDefault: false,
2507     isContainer : false,
2508     active : false,
2509     fa: false,
2510     
2511     getAutoCreate : function(){
2512         
2513         if(this.isContainer){
2514             return {
2515                 tag: 'li',
2516                 cls: 'dropdown-menu-item dropdown-item'
2517             };
2518         }
2519         var ctag = {
2520             tag: 'span',
2521             html: 'Link'
2522         };
2523         
2524         var anc = {
2525             tag : 'a',
2526             href : '#',
2527             cn : [  ]
2528         };
2529         
2530         if (this.fa !== false) {
2531             anc.cn.push({
2532                 tag : 'i',
2533                 cls : 'fa fa-' + this.fa
2534             });
2535         }
2536         
2537         anc.cn.push(ctag);
2538         
2539         
2540         var cfg= {
2541             tag: 'li',
2542             cls: 'dropdown-menu-item dropdown-item',
2543             cn: [ anc ]
2544         };
2545         if (this.parent().type == 'treeview') {
2546             cfg.cls = 'treeview-menu';
2547         }
2548         if (this.active) {
2549             cfg.cls += ' active';
2550         }
2551         
2552         
2553         
2554         anc.href = this.href || cfg.cn[0].href ;
2555         ctag.html = this.html || cfg.cn[0].html ;
2556         return cfg;
2557     },
2558     
2559     initEvents: function()
2560     {
2561         if (this.parent().type == 'treeview') {
2562             this.el.select('a').on('click', this.onClick, this);
2563         }
2564         
2565         if (this.menu) {
2566             this.menu.parentType = this.xtype;
2567             this.menu.triggerEl = this.el;
2568             this.menu = this.addxtype(Roo.apply({}, this.menu));
2569         }
2570         
2571     },
2572     onClick : function(e)
2573     {
2574         Roo.log('item on click ');
2575         
2576         if(this.preventDefault){
2577             e.preventDefault();
2578         }
2579         //this.parent().hideMenuItems();
2580         
2581         this.fireEvent('click', this, e);
2582     },
2583     getEl : function()
2584     {
2585         return this.el;
2586     } 
2587 });
2588
2589  
2590
2591  /*
2592  * - LGPL
2593  *
2594  * menu separator
2595  * 
2596  */
2597
2598
2599 /**
2600  * @class Roo.bootstrap.MenuSeparator
2601  * @extends Roo.bootstrap.Component
2602  * Bootstrap MenuSeparator class
2603  * 
2604  * @constructor
2605  * Create a new MenuItem
2606  * @param {Object} config The config object
2607  */
2608
2609
2610 Roo.bootstrap.MenuSeparator = function(config){
2611     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2612 };
2613
2614 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2615     
2616     getAutoCreate : function(){
2617         var cfg = {
2618             cls: 'divider',
2619             tag : 'li'
2620         };
2621         
2622         return cfg;
2623     }
2624    
2625 });
2626
2627  
2628
2629  
2630 /*
2631 * Licence: LGPL
2632 */
2633
2634 /**
2635  * @class Roo.bootstrap.Modal
2636  * @extends Roo.bootstrap.Component
2637  * Bootstrap Modal class
2638  * @cfg {String} title Title of dialog
2639  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2640  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2641  * @cfg {Boolean} specificTitle default false
2642  * @cfg {Array} buttons Array of buttons or standard button set..
2643  * @cfg {String} buttonPosition (left|right|center) default right
2644  * @cfg {Boolean} animate default true
2645  * @cfg {Boolean} allow_close default true
2646  * @cfg {Boolean} fitwindow default false
2647  * @cfg {String} size (sm|lg) default empty
2648  * @cfg {Number} max_width set the max width of modal
2649  *
2650  *
2651  * @constructor
2652  * Create a new Modal Dialog
2653  * @param {Object} config The config object
2654  */
2655
2656 Roo.bootstrap.Modal = function(config){
2657     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2658     this.addEvents({
2659         // raw events
2660         /**
2661          * @event btnclick
2662          * The raw btnclick event for the button
2663          * @param {Roo.EventObject} e
2664          */
2665         "btnclick" : true,
2666         /**
2667          * @event resize
2668          * Fire when dialog resize
2669          * @param {Roo.bootstrap.Modal} this
2670          * @param {Roo.EventObject} e
2671          */
2672         "resize" : true
2673     });
2674     this.buttons = this.buttons || [];
2675
2676     if (this.tmpl) {
2677         this.tmpl = Roo.factory(this.tmpl);
2678     }
2679
2680 };
2681
2682 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2683
2684     title : 'test dialog',
2685
2686     buttons : false,
2687
2688     // set on load...
2689
2690     html: false,
2691
2692     tmp: false,
2693
2694     specificTitle: false,
2695
2696     buttonPosition: 'right',
2697
2698     allow_close : true,
2699
2700     animate : true,
2701
2702     fitwindow: false,
2703     
2704      // private
2705     dialogEl: false,
2706     bodyEl:  false,
2707     footerEl:  false,
2708     titleEl:  false,
2709     closeEl:  false,
2710
2711     size: '',
2712     
2713     max_width: 0,
2714     
2715     max_height: 0,
2716     
2717     fit_content: false,
2718
2719     onRender : function(ct, position)
2720     {
2721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2722
2723         if(!this.el){
2724             var cfg = Roo.apply({},  this.getAutoCreate());
2725             cfg.id = Roo.id();
2726             //if(!cfg.name){
2727             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2728             //}
2729             //if (!cfg.name.length) {
2730             //    delete cfg.name;
2731            // }
2732             if (this.cls) {
2733                 cfg.cls += ' ' + this.cls;
2734             }
2735             if (this.style) {
2736                 cfg.style = this.style;
2737             }
2738             this.el = Roo.get(document.body).createChild(cfg, position);
2739         }
2740         //var type = this.el.dom.type;
2741
2742
2743         if(this.tabIndex !== undefined){
2744             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2745         }
2746
2747         this.dialogEl = this.el.select('.modal-dialog',true).first();
2748         this.bodyEl = this.el.select('.modal-body',true).first();
2749         this.closeEl = this.el.select('.modal-header .close', true).first();
2750         this.headerEl = this.el.select('.modal-header',true).first();
2751         this.titleEl = this.el.select('.modal-title',true).first();
2752         this.footerEl = this.el.select('.modal-footer',true).first();
2753
2754         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2755         
2756         //this.el.addClass("x-dlg-modal");
2757
2758         if (this.buttons.length) {
2759             Roo.each(this.buttons, function(bb) {
2760                 var b = Roo.apply({}, bb);
2761                 b.xns = b.xns || Roo.bootstrap;
2762                 b.xtype = b.xtype || 'Button';
2763                 if (typeof(b.listeners) == 'undefined') {
2764                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2765                 }
2766
2767                 var btn = Roo.factory(b);
2768
2769                 btn.render(this.el.select('.modal-footer div').first());
2770
2771             },this);
2772         }
2773         // render the children.
2774         var nitems = [];
2775
2776         if(typeof(this.items) != 'undefined'){
2777             var items = this.items;
2778             delete this.items;
2779
2780             for(var i =0;i < items.length;i++) {
2781                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2782             }
2783         }
2784
2785         this.items = nitems;
2786
2787         // where are these used - they used to be body/close/footer
2788
2789
2790         this.initEvents();
2791         //this.el.addClass([this.fieldClass, this.cls]);
2792
2793     },
2794
2795     getAutoCreate : function()
2796     {
2797         var bdy = {
2798                 cls : 'modal-body',
2799                 html : this.html || ''
2800         };
2801
2802         var title = {
2803             tag: 'h4',
2804             cls : 'modal-title',
2805             html : this.title
2806         };
2807
2808         if(this.specificTitle){
2809             title = this.title;
2810
2811         };
2812
2813         var header = [];
2814         if (this.allow_close && Roo.bootstrap.version == 3) {
2815             header.push({
2816                 tag: 'button',
2817                 cls : 'close',
2818                 html : '&times'
2819             });
2820         }
2821
2822         header.push(title);
2823
2824         if (this.allow_close && Roo.bootstrap.version == 4) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831         
2832         var size = '';
2833
2834         if(this.size.length){
2835             size = 'modal-' + this.size;
2836         }
2837
2838         var modal = {
2839             cls: "modal",
2840              cn : [
2841                 {
2842                     cls: "modal-dialog " + size,
2843                     cn : [
2844                         {
2845                             cls : "modal-content",
2846                             cn : [
2847                                 {
2848                                     cls : 'modal-header',
2849                                     cn : header
2850                                 },
2851                                 bdy,
2852                                 {
2853                                     cls : 'modal-footer',
2854                                     cn : [
2855                                         {
2856                                             tag: 'div',
2857                                             cls: 'btn-' + this.buttonPosition
2858                                         }
2859                                     ]
2860
2861                                 }
2862
2863
2864                             ]
2865
2866                         }
2867                     ]
2868
2869                 }
2870             ]
2871         };
2872
2873         if(this.animate){
2874             modal.cls += ' fade';
2875         }
2876
2877         return modal;
2878
2879     },
2880     getChildContainer : function() {
2881
2882          return this.bodyEl;
2883
2884     },
2885     getButtonContainer : function() {
2886          return this.el.select('.modal-footer div',true).first();
2887
2888     },
2889     initEvents : function()
2890     {
2891         if (this.allow_close) {
2892             this.closeEl.on('click', this.hide, this);
2893         }
2894         Roo.EventManager.onWindowResize(this.resize, this, true);
2895
2896
2897     },
2898
2899     resize : function()
2900     {
2901         this.maskEl.setSize(
2902             Roo.lib.Dom.getViewWidth(true),
2903             Roo.lib.Dom.getViewHeight(true)
2904         );
2905         
2906         if (this.fitwindow) {
2907             this.setSize(
2908                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2909                 this.height || Roo.lib.Dom.getViewportHeight(true) - 60
2910             );
2911             return;
2912         }
2913         
2914         if(this.max_width !== 0) {
2915             
2916             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2917             
2918             if(this.height) {
2919                 this.setSize(w, this.height);
2920                 return;
2921             }
2922             
2923             if(this.max_height) {
2924                 this.setSize(w,Math.min(
2925                     this.max_height,
2926                     Roo.lib.Dom.getViewportHeight(true) - 60
2927                 ));
2928                 
2929                 return;
2930             }
2931             
2932             if(!this.fit_content) {
2933                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2934                 return;
2935             }
2936             
2937             this.setSize(w, Math.min(
2938                 60 +
2939                 this.headerEl.getHeight() + 
2940                 this.footerEl.getHeight() + 
2941                 this.getChildHeight(this.bodyEl.dom.childNodes),
2942                 Roo.lib.Dom.getViewportHeight(true) - 60)
2943             );
2944         }
2945         
2946     },
2947
2948     setSize : function(w,h)
2949     {
2950         if (!w && !h) {
2951             return;
2952         }
2953         
2954         this.resizeTo(w,h);
2955     },
2956
2957     show : function() {
2958
2959         if (!this.rendered) {
2960             this.render();
2961         }
2962
2963         //this.el.setStyle('display', 'block');
2964         this.el.removeClass('hideing');
2965         this.el.dom.style.display='block';
2966         
2967         Roo.get(document.body).addClass('modal-open');
2968  
2969         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2970             var _this = this;
2971             (function(){
2972                 this.el.addClass('show');
2973                 this.el.addClass('in');
2974             }).defer(50, this);
2975         }else{
2976             this.el.addClass('show');
2977             this.el.addClass('in');
2978         }
2979
2980         // not sure how we can show data in here..
2981         //if (this.tmpl) {
2982         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2983         //}
2984
2985         Roo.get(document.body).addClass("x-body-masked");
2986         
2987         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2988         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2989         this.maskEl.dom.style.display = 'block';
2990         this.maskEl.addClass('show');
2991         
2992         
2993         this.resize();
2994         
2995         this.fireEvent('show', this);
2996
2997         // set zindex here - otherwise it appears to be ignored...
2998         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2999
3000         (function () {
3001             this.items.forEach( function(e) {
3002                 e.layout ? e.layout() : false;
3003
3004             });
3005         }).defer(100,this);
3006
3007     },
3008     hide : function()
3009     {
3010         if(this.fireEvent("beforehide", this) !== false){
3011             
3012             this.maskEl.removeClass('show');
3013             
3014             this.maskEl.dom.style.display = '';
3015             Roo.get(document.body).removeClass("x-body-masked");
3016             this.el.removeClass('in');
3017             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3018
3019             if(this.animate){ // why
3020                 this.el.addClass('hideing');
3021                 this.el.removeClass('show');
3022                 (function(){
3023                     if (!this.el.hasClass('hideing')) {
3024                         return; // it's been shown again...
3025                     }
3026                     
3027                     this.el.dom.style.display='';
3028
3029                     Roo.get(document.body).removeClass('modal-open');
3030                     this.el.removeClass('hideing');
3031                 }).defer(150,this);
3032                 
3033             }else{
3034                 this.el.removeClass('show');
3035                 this.el.dom.style.display='';
3036                 Roo.get(document.body).removeClass('modal-open');
3037
3038             }
3039             this.fireEvent('hide', this);
3040         }
3041     },
3042     isVisible : function()
3043     {
3044         
3045         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3046         
3047     },
3048
3049     addButton : function(str, cb)
3050     {
3051
3052
3053         var b = Roo.apply({}, { html : str } );
3054         b.xns = b.xns || Roo.bootstrap;
3055         b.xtype = b.xtype || 'Button';
3056         if (typeof(b.listeners) == 'undefined') {
3057             b.listeners = { click : cb.createDelegate(this)  };
3058         }
3059
3060         var btn = Roo.factory(b);
3061
3062         btn.render(this.el.select('.modal-footer div').first());
3063
3064         return btn;
3065
3066     },
3067
3068     setDefaultButton : function(btn)
3069     {
3070         //this.el.select('.modal-footer').()
3071     },
3072     diff : false,
3073
3074     resizeTo: function(w,h)
3075     {
3076         // skip.. ?? why??
3077
3078         this.dialogEl.setWidth(w);
3079         if (this.diff === false) {
3080             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
3081         }
3082
3083         this.bodyEl.setHeight(h - this.diff);
3084
3085         this.fireEvent('resize', this);
3086
3087     },
3088     setContentSize  : function(w, h)
3089     {
3090
3091     },
3092     onButtonClick: function(btn,e)
3093     {
3094         //Roo.log([a,b,c]);
3095         this.fireEvent('btnclick', btn.name, e);
3096     },
3097      /**
3098      * Set the title of the Dialog
3099      * @param {String} str new Title
3100      */
3101     setTitle: function(str) {
3102         this.titleEl.dom.innerHTML = str;
3103     },
3104     /**
3105      * Set the body of the Dialog
3106      * @param {String} str new Title
3107      */
3108     setBody: function(str) {
3109         this.bodyEl.dom.innerHTML = str;
3110     },
3111     /**
3112      * Set the body of the Dialog using the template
3113      * @param {Obj} data - apply this data to the template and replace the body contents.
3114      */
3115     applyBody: function(obj)
3116     {
3117         if (!this.tmpl) {
3118             Roo.log("Error - using apply Body without a template");
3119             //code
3120         }
3121         this.tmpl.overwrite(this.bodyEl, obj);
3122     },
3123     
3124     getChildHeight : function(child_nodes)
3125     {
3126         if(
3127             !child_nodes ||
3128             child_nodes.length == 0
3129         ) {
3130             return;
3131         }
3132         
3133         var child_height = 0;
3134         
3135         for(var i = 0; i < child_nodes.length; i++) {
3136             
3137             /*
3138             * for modal with tabs...
3139             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3140                 
3141                 var layout_childs = child_nodes[i].childNodes;
3142                 
3143                 for(var j = 0; j < layout_childs.length; j++) {
3144                     
3145                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3146                         
3147                         var layout_body_childs = layout_childs[j].childNodes;
3148                         
3149                         for(var k = 0; k < layout_body_childs.length; k++) {
3150                             
3151                             if(layout_body_childs[k].classList.contains('navbar')) {
3152                                 child_height += layout_body_childs[k].offsetHeight;
3153                                 continue;
3154                             }
3155                             
3156                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3157                                 
3158                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3159                                 
3160                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3161                                     
3162                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3163                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3164                                         continue;
3165                                     }
3166                                     
3167                                 }
3168                                 
3169                             }
3170                             
3171                         }
3172                     }
3173                 }
3174                 continue;
3175             }
3176             */
3177             
3178             child_height += child_nodes[i].offsetHeight;
3179             // Roo.log(child_nodes[i].offsetHeight);
3180         }
3181         
3182         return child_height;
3183     }
3184
3185 });
3186
3187
3188 Roo.apply(Roo.bootstrap.Modal,  {
3189     /**
3190          * Button config that displays a single OK button
3191          * @type Object
3192          */
3193         OK :  [{
3194             name : 'ok',
3195             weight : 'primary',
3196             html : 'OK'
3197         }],
3198         /**
3199          * Button config that displays Yes and No buttons
3200          * @type Object
3201          */
3202         YESNO : [
3203             {
3204                 name  : 'no',
3205                 html : 'No'
3206             },
3207             {
3208                 name  :'yes',
3209                 weight : 'primary',
3210                 html : 'Yes'
3211             }
3212         ],
3213
3214         /**
3215          * Button config that displays OK and Cancel buttons
3216          * @type Object
3217          */
3218         OKCANCEL : [
3219             {
3220                name : 'cancel',
3221                 html : 'Cancel'
3222             },
3223             {
3224                 name : 'ok',
3225                 weight : 'primary',
3226                 html : 'OK'
3227             }
3228         ],
3229         /**
3230          * Button config that displays Yes, No and Cancel buttons
3231          * @type Object
3232          */
3233         YESNOCANCEL : [
3234             {
3235                 name : 'yes',
3236                 weight : 'primary',
3237                 html : 'Yes'
3238             },
3239             {
3240                 name : 'no',
3241                 html : 'No'
3242             },
3243             {
3244                 name : 'cancel',
3245                 html : 'Cancel'
3246             }
3247         ],
3248         
3249         zIndex : 10001
3250 });
3251 /*
3252  * - LGPL
3253  *
3254  * messagebox - can be used as a replace
3255  * 
3256  */
3257 /**
3258  * @class Roo.MessageBox
3259  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3260  * Example usage:
3261  *<pre><code>
3262 // Basic alert:
3263 Roo.Msg.alert('Status', 'Changes saved successfully.');
3264
3265 // Prompt for user data:
3266 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3267     if (btn == 'ok'){
3268         // process text value...
3269     }
3270 });
3271
3272 // Show a dialog using config options:
3273 Roo.Msg.show({
3274    title:'Save Changes?',
3275    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3276    buttons: Roo.Msg.YESNOCANCEL,
3277    fn: processResult,
3278    animEl: 'elId'
3279 });
3280 </code></pre>
3281  * @singleton
3282  */
3283 Roo.bootstrap.MessageBox = function(){
3284     var dlg, opt, mask, waitTimer;
3285     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3286     var buttons, activeTextEl, bwidth;
3287
3288     
3289     // private
3290     var handleButton = function(button){
3291         dlg.hide();
3292         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3293     };
3294
3295     // private
3296     var handleHide = function(){
3297         if(opt && opt.cls){
3298             dlg.el.removeClass(opt.cls);
3299         }
3300         //if(waitTimer){
3301         //    Roo.TaskMgr.stop(waitTimer);
3302         //    waitTimer = null;
3303         //}
3304     };
3305
3306     // private
3307     var updateButtons = function(b){
3308         var width = 0;
3309         if(!b){
3310             buttons["ok"].hide();
3311             buttons["cancel"].hide();
3312             buttons["yes"].hide();
3313             buttons["no"].hide();
3314             //dlg.footer.dom.style.display = 'none';
3315             return width;
3316         }
3317         dlg.footerEl.dom.style.display = '';
3318         for(var k in buttons){
3319             if(typeof buttons[k] != "function"){
3320                 if(b[k]){
3321                     buttons[k].show();
3322                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3323                     width += buttons[k].el.getWidth()+15;
3324                 }else{
3325                     buttons[k].hide();
3326                 }
3327             }
3328         }
3329         return width;
3330     };
3331
3332     // private
3333     var handleEsc = function(d, k, e){
3334         if(opt && opt.closable !== false){
3335             dlg.hide();
3336         }
3337         if(e){
3338             e.stopEvent();
3339         }
3340     };
3341
3342     return {
3343         /**
3344          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3345          * @return {Roo.BasicDialog} The BasicDialog element
3346          */
3347         getDialog : function(){
3348            if(!dlg){
3349                 dlg = new Roo.bootstrap.Modal( {
3350                     //draggable: true,
3351                     //resizable:false,
3352                     //constraintoviewport:false,
3353                     //fixedcenter:true,
3354                     //collapsible : false,
3355                     //shim:true,
3356                     //modal: true,
3357                 //    width: 'auto',
3358                   //  height:100,
3359                     //buttonAlign:"center",
3360                     closeClick : function(){
3361                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3362                             handleButton("no");
3363                         }else{
3364                             handleButton("cancel");
3365                         }
3366                     }
3367                 });
3368                 dlg.render();
3369                 dlg.on("hide", handleHide);
3370                 mask = dlg.mask;
3371                 //dlg.addKeyListener(27, handleEsc);
3372                 buttons = {};
3373                 this.buttons = buttons;
3374                 var bt = this.buttonText;
3375                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3376                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3377                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3378                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3379                 //Roo.log(buttons);
3380                 bodyEl = dlg.bodyEl.createChild({
3381
3382                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3383                         '<textarea class="roo-mb-textarea"></textarea>' +
3384                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3385                 });
3386                 msgEl = bodyEl.dom.firstChild;
3387                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3388                 textboxEl.enableDisplayMode();
3389                 textboxEl.addKeyListener([10,13], function(){
3390                     if(dlg.isVisible() && opt && opt.buttons){
3391                         if(opt.buttons.ok){
3392                             handleButton("ok");
3393                         }else if(opt.buttons.yes){
3394                             handleButton("yes");
3395                         }
3396                     }
3397                 });
3398                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3399                 textareaEl.enableDisplayMode();
3400                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3401                 progressEl.enableDisplayMode();
3402                 
3403                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3404                 var pf = progressEl.dom.firstChild;
3405                 if (pf) {
3406                     pp = Roo.get(pf.firstChild);
3407                     pp.setHeight(pf.offsetHeight);
3408                 }
3409                 
3410             }
3411             return dlg;
3412         },
3413
3414         /**
3415          * Updates the message box body text
3416          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3417          * the XHTML-compliant non-breaking space character '&amp;#160;')
3418          * @return {Roo.MessageBox} This message box
3419          */
3420         updateText : function(text)
3421         {
3422             if(!dlg.isVisible() && !opt.width){
3423                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3424                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3425             }
3426             msgEl.innerHTML = text || '&#160;';
3427       
3428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3430             var w = Math.max(
3431                     Math.min(opt.width || cw , this.maxWidth), 
3432                     Math.max(opt.minWidth || this.minWidth, bwidth)
3433             );
3434             if(opt.prompt){
3435                 activeTextEl.setWidth(w);
3436             }
3437             if(dlg.isVisible()){
3438                 dlg.fixedcenter = false;
3439             }
3440             // to big, make it scroll. = But as usual stupid IE does not support
3441             // !important..
3442             
3443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3446             } else {
3447                 bodyEl.dom.style.height = '';
3448                 bodyEl.dom.style.overflowY = '';
3449             }
3450             if (cw > w) {
3451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3452             } else {
3453                 bodyEl.dom.style.overflowX = '';
3454             }
3455             
3456             dlg.setContentSize(w, bodyEl.getHeight());
3457             if(dlg.isVisible()){
3458                 dlg.fixedcenter = true;
3459             }
3460             return this;
3461         },
3462
3463         /**
3464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3468          * @return {Roo.MessageBox} This message box
3469          */
3470         updateProgress : function(value, text){
3471             if(text){
3472                 this.updateText(text);
3473             }
3474             
3475             if (pp) { // weird bug on my firefox - for some reason this is not defined
3476                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3477                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3478             }
3479             return this;
3480         },        
3481
3482         /**
3483          * Returns true if the message box is currently displayed
3484          * @return {Boolean} True if the message box is visible, else false
3485          */
3486         isVisible : function(){
3487             return dlg && dlg.isVisible();  
3488         },
3489
3490         /**
3491          * Hides the message box if it is displayed
3492          */
3493         hide : function(){
3494             if(this.isVisible()){
3495                 dlg.hide();
3496             }  
3497         },
3498
3499         /**
3500          * Displays a new message box, or reinitializes an existing message box, based on the config options
3501          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3502          * The following config object properties are supported:
3503          * <pre>
3504 Property    Type             Description
3505 ----------  ---------------  ------------------------------------------------------------------------------------
3506 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3507                                    closes (defaults to undefined)
3508 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3509                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3510 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3511                                    progress and wait dialogs will ignore this property and always hide the
3512                                    close button as they can only be closed programmatically.
3513 cls               String           A custom CSS class to apply to the message box element
3514 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3515                                    displayed (defaults to 75)
3516 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3517                                    function will be btn (the name of the button that was clicked, if applicable,
3518                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3519                                    Progress and wait dialogs will ignore this option since they do not respond to
3520                                    user actions and can only be closed programmatically, so any required function
3521                                    should be called by the same code after it closes the dialog.
3522 icon              String           A CSS class that provides a background image to be used as an icon for
3523                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3524 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3525 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3526 modal             Boolean          False to allow user interaction with the page while the message box is
3527                                    displayed (defaults to true)
3528 msg               String           A string that will replace the existing message box body text (defaults
3529                                    to the XHTML-compliant non-breaking space character '&#160;')
3530 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3531 progress          Boolean          True to display a progress bar (defaults to false)
3532 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3533 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3534 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3535 title             String           The title text
3536 value             String           The string value to set into the active textbox element if displayed
3537 wait              Boolean          True to display a progress bar (defaults to false)
3538 width             Number           The width of the dialog in pixels
3539 </pre>
3540          *
3541          * Example usage:
3542          * <pre><code>
3543 Roo.Msg.show({
3544    title: 'Address',
3545    msg: 'Please enter your address:',
3546    width: 300,
3547    buttons: Roo.MessageBox.OKCANCEL,
3548    multiline: true,
3549    fn: saveAddress,
3550    animEl: 'addAddressBtn'
3551 });
3552 </code></pre>
3553          * @param {Object} config Configuration options
3554          * @return {Roo.MessageBox} This message box
3555          */
3556         show : function(options)
3557         {
3558             
3559             // this causes nightmares if you show one dialog after another
3560             // especially on callbacks..
3561              
3562             if(this.isVisible()){
3563                 
3564                 this.hide();
3565                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3566                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3567                 Roo.log("New Dialog Message:" +  options.msg )
3568                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3569                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3570                 
3571             }
3572             var d = this.getDialog();
3573             opt = options;
3574             d.setTitle(opt.title || "&#160;");
3575             d.closeEl.setDisplayed(opt.closable !== false);
3576             activeTextEl = textboxEl;
3577             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3578             if(opt.prompt){
3579                 if(opt.multiline){
3580                     textboxEl.hide();
3581                     textareaEl.show();
3582                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3583                         opt.multiline : this.defaultTextHeight);
3584                     activeTextEl = textareaEl;
3585                 }else{
3586                     textboxEl.show();
3587                     textareaEl.hide();
3588                 }
3589             }else{
3590                 textboxEl.hide();
3591                 textareaEl.hide();
3592             }
3593             progressEl.setDisplayed(opt.progress === true);
3594             this.updateProgress(0);
3595             activeTextEl.dom.value = opt.value || "";
3596             if(opt.prompt){
3597                 dlg.setDefaultButton(activeTextEl);
3598             }else{
3599                 var bs = opt.buttons;
3600                 var db = null;
3601                 if(bs && bs.ok){
3602                     db = buttons["ok"];
3603                 }else if(bs && bs.yes){
3604                     db = buttons["yes"];
3605                 }
3606                 dlg.setDefaultButton(db);
3607             }
3608             bwidth = updateButtons(opt.buttons);
3609             this.updateText(opt.msg);
3610             if(opt.cls){
3611                 d.el.addClass(opt.cls);
3612             }
3613             d.proxyDrag = opt.proxyDrag === true;
3614             d.modal = opt.modal !== false;
3615             d.mask = opt.modal !== false ? mask : false;
3616             if(!d.isVisible()){
3617                 // force it to the end of the z-index stack so it gets a cursor in FF
3618                 document.body.appendChild(dlg.el.dom);
3619                 d.animateTarget = null;
3620                 d.show(options.animEl);
3621             }
3622             return this;
3623         },
3624
3625         /**
3626          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3627          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3628          * and closing the message box when the process is complete.
3629          * @param {String} title The title bar text
3630          * @param {String} msg The message box body text
3631          * @return {Roo.MessageBox} This message box
3632          */
3633         progress : function(title, msg){
3634             this.show({
3635                 title : title,
3636                 msg : msg,
3637                 buttons: false,
3638                 progress:true,
3639                 closable:false,
3640                 minWidth: this.minProgressWidth,
3641                 modal : true
3642             });
3643             return this;
3644         },
3645
3646         /**
3647          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3648          * If a callback function is passed it will be called after the user clicks the button, and the
3649          * id of the button that was clicked will be passed as the only parameter to the callback
3650          * (could also be the top-right close button).
3651          * @param {String} title The title bar text
3652          * @param {String} msg The message box body text
3653          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3654          * @param {Object} scope (optional) The scope of the callback function
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         alert : function(title, msg, fn, scope)
3658         {
3659             this.show({
3660                 title : title,
3661                 msg : msg,
3662                 buttons: this.OK,
3663                 fn: fn,
3664                 closable : false,
3665                 scope : scope,
3666                 modal : true
3667             });
3668             return this;
3669         },
3670
3671         /**
3672          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3673          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3674          * You are responsible for closing the message box when the process is complete.
3675          * @param {String} msg The message box body text
3676          * @param {String} title (optional) The title bar text
3677          * @return {Roo.MessageBox} This message box
3678          */
3679         wait : function(msg, title){
3680             this.show({
3681                 title : title,
3682                 msg : msg,
3683                 buttons: false,
3684                 closable:false,
3685                 progress:true,
3686                 modal:true,
3687                 width:300,
3688                 wait:true
3689             });
3690             waitTimer = Roo.TaskMgr.start({
3691                 run: function(i){
3692                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3693                 },
3694                 interval: 1000
3695             });
3696             return this;
3697         },
3698
3699         /**
3700          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3701          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3702          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3703          * @param {String} title The title bar text
3704          * @param {String} msg The message box body text
3705          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3706          * @param {Object} scope (optional) The scope of the callback function
3707          * @return {Roo.MessageBox} This message box
3708          */
3709         confirm : function(title, msg, fn, scope){
3710             this.show({
3711                 title : title,
3712                 msg : msg,
3713                 buttons: this.YESNO,
3714                 fn: fn,
3715                 scope : scope,
3716                 modal : true
3717             });
3718             return this;
3719         },
3720
3721         /**
3722          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3723          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3724          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3725          * (could also be the top-right close button) and the text that was entered will be passed as the two
3726          * parameters to the callback.
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3732          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3733          * @return {Roo.MessageBox} This message box
3734          */
3735         prompt : function(title, msg, fn, scope, multiline){
3736             this.show({
3737                 title : title,
3738                 msg : msg,
3739                 buttons: this.OKCANCEL,
3740                 fn: fn,
3741                 minWidth:250,
3742                 scope : scope,
3743                 prompt:true,
3744                 multiline: multiline,
3745                 modal : true
3746             });
3747             return this;
3748         },
3749
3750         /**
3751          * Button config that displays a single OK button
3752          * @type Object
3753          */
3754         OK : {ok:true},
3755         /**
3756          * Button config that displays Yes and No buttons
3757          * @type Object
3758          */
3759         YESNO : {yes:true, no:true},
3760         /**
3761          * Button config that displays OK and Cancel buttons
3762          * @type Object
3763          */
3764         OKCANCEL : {ok:true, cancel:true},
3765         /**
3766          * Button config that displays Yes, No and Cancel buttons
3767          * @type Object
3768          */
3769         YESNOCANCEL : {yes:true, no:true, cancel:true},
3770
3771         /**
3772          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3773          * @type Number
3774          */
3775         defaultTextHeight : 75,
3776         /**
3777          * The maximum width in pixels of the message box (defaults to 600)
3778          * @type Number
3779          */
3780         maxWidth : 600,
3781         /**
3782          * The minimum width in pixels of the message box (defaults to 100)
3783          * @type Number
3784          */
3785         minWidth : 100,
3786         /**
3787          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3788          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3789          * @type Number
3790          */
3791         minProgressWidth : 250,
3792         /**
3793          * An object containing the default button text strings that can be overriden for localized language support.
3794          * Supported properties are: ok, cancel, yes and no.
3795          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3796          * @type Object
3797          */
3798         buttonText : {
3799             ok : "OK",
3800             cancel : "Cancel",
3801             yes : "Yes",
3802             no : "No"
3803         }
3804     };
3805 }();
3806
3807 /**
3808  * Shorthand for {@link Roo.MessageBox}
3809  */
3810 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3811 Roo.Msg = Roo.Msg || Roo.MessageBox;
3812 /*
3813  * - LGPL
3814  *
3815  * navbar
3816  * 
3817  */
3818
3819 /**
3820  * @class Roo.bootstrap.Navbar
3821  * @extends Roo.bootstrap.Component
3822  * Bootstrap Navbar class
3823
3824  * @constructor
3825  * Create a new Navbar
3826  * @param {Object} config The config object
3827  */
3828
3829
3830 Roo.bootstrap.Navbar = function(config){
3831     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3832     this.addEvents({
3833         // raw events
3834         /**
3835          * @event beforetoggle
3836          * Fire before toggle the menu
3837          * @param {Roo.EventObject} e
3838          */
3839         "beforetoggle" : true
3840     });
3841 };
3842
3843 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3844     
3845     
3846    
3847     // private
3848     navItems : false,
3849     loadMask : false,
3850     
3851     
3852     getAutoCreate : function(){
3853         
3854         
3855         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3856         
3857     },
3858     
3859     initEvents :function ()
3860     {
3861         //Roo.log(this.el.select('.navbar-toggle',true));
3862         this.el.select('.navbar-toggle',true).on('click', function() {
3863             if(this.fireEvent('beforetoggle', this) !== false){
3864                 var ce = this.el.select('.navbar-collapse',true).first();
3865                 ce.toggleClass('in'); // old...
3866                 if (ce.hasClass('collapse')) {
3867                     ce.removeClass('collapse');
3868                     ce.addClass('collapsing');
3869                 } else {
3870                
3871                     ce.addClass('collapse');
3872                 }
3873             }
3874             
3875         }, this);
3876         
3877         var mark = {
3878             tag: "div",
3879             cls:"x-dlg-mask"
3880         };
3881         
3882         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3883         
3884         var size = this.el.getSize();
3885         this.maskEl.setSize(size.width, size.height);
3886         this.maskEl.enableDisplayMode("block");
3887         this.maskEl.hide();
3888         
3889         if(this.loadMask){
3890             this.maskEl.show();
3891         }
3892     },
3893     
3894     
3895     getChildContainer : function()
3896     {
3897         if (this.el.select('.collapse').getCount()) {
3898             return this.el.select('.collapse',true).first();
3899         }
3900         
3901         return this.el;
3902     },
3903     
3904     mask : function()
3905     {
3906         this.maskEl.show();
3907     },
3908     
3909     unmask : function()
3910     {
3911         this.maskEl.hide();
3912     } 
3913     
3914     
3915     
3916     
3917 });
3918
3919
3920
3921  
3922
3923  /*
3924  * - LGPL
3925  *
3926  * navbar
3927  * 
3928  */
3929
3930 /**
3931  * @class Roo.bootstrap.NavSimplebar
3932  * @extends Roo.bootstrap.Navbar
3933  * Bootstrap Sidebar class
3934  *
3935  * @cfg {Boolean} inverse is inverted color
3936  * 
3937  * @cfg {String} type (nav | pills | tabs)
3938  * @cfg {Boolean} arrangement stacked | justified
3939  * @cfg {String} align (left | right) alignment
3940  * 
3941  * @cfg {Boolean} main (true|false) main nav bar? default false
3942  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3943  * 
3944  * @cfg {String} tag (header|footer|nav|div) default is nav 
3945
3946  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3947  * 
3948  * 
3949  * @constructor
3950  * Create a new Sidebar
3951  * @param {Object} config The config object
3952  */
3953
3954
3955 Roo.bootstrap.NavSimplebar = function(config){
3956     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3957 };
3958
3959 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3960     
3961     inverse: false,
3962     
3963     type: false,
3964     arrangement: '',
3965     align : false,
3966     
3967     weight : 'light',
3968     
3969     main : false,
3970     
3971     
3972     tag : false,
3973     
3974     
3975     getAutoCreate : function(){
3976         
3977         
3978         var cfg = {
3979             tag : this.tag || 'div',
3980             cls : 'navbar navbar-expand-lg'
3981         };
3982         if (['light','white'].indexOf(this.weight) > -1) {
3983             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
3984         }
3985         cfg.cls += ' bg-' + this.weight;
3986         
3987           
3988         
3989         cfg.cn = [
3990             {
3991                 cls: 'nav',
3992                 tag : 'ul'
3993             }
3994         ];
3995         
3996          
3997         this.type = this.type || 'nav';
3998         if (['tabs','pills'].indexOf(this.type)!==-1) {
3999             cfg.cn[0].cls += ' nav-' + this.type
4000         
4001         
4002         } else {
4003             if (this.type!=='nav') {
4004                 Roo.log('nav type must be nav/tabs/pills')
4005             }
4006             cfg.cn[0].cls += ' navbar-nav'
4007         }
4008         
4009         
4010         
4011         
4012         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
4013             cfg.cn[0].cls += ' nav-' + this.arrangement;
4014         }
4015         
4016         
4017         if (this.align === 'right') {
4018             cfg.cn[0].cls += ' navbar-right';
4019         }
4020         
4021         if (this.inverse) {
4022             cfg.cls += ' navbar-inverse';
4023             
4024         }
4025         
4026         
4027         return cfg;
4028     
4029         
4030     }
4031     
4032     
4033     
4034 });
4035
4036
4037
4038  
4039
4040  
4041        /*
4042  * - LGPL
4043  *
4044  * navbar
4045  * navbar-fixed-top
4046  * navbar-expand-md  fixed-top 
4047  */
4048
4049 /**
4050  * @class Roo.bootstrap.NavHeaderbar
4051  * @extends Roo.bootstrap.NavSimplebar
4052  * Bootstrap Sidebar class
4053  *
4054  * @cfg {String} brand what is brand
4055  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4056  * @cfg {String} brand_href href of the brand
4057  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4058  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4059  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4060  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4061  * 
4062  * @constructor
4063  * Create a new Sidebar
4064  * @param {Object} config The config object
4065  */
4066
4067
4068 Roo.bootstrap.NavHeaderbar = function(config){
4069     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4070       
4071 };
4072
4073 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4074     
4075     position: '',
4076     brand: '',
4077     brand_href: false,
4078     srButton : true,
4079     autohide : false,
4080     desktopCenter : false,
4081    
4082     
4083     getAutoCreate : function(){
4084         
4085         var   cfg = {
4086             tag: this.nav || 'nav',
4087             cls: 'navbar navbar-expand-md',
4088             role: 'navigation',
4089             cn: []
4090         };
4091         
4092         var cn = cfg.cn;
4093         if (this.desktopCenter) {
4094             cn.push({cls : 'container', cn : []});
4095             cn = cn[0].cn;
4096         }
4097         
4098         if(this.srButton){
4099             var btn = {
4100                 tag: 'button',
4101                 type: 'button',
4102                 cls: 'navbar-toggle navbar-toggler',
4103                 'data-toggle': 'collapse',
4104                 cn: [
4105                     {
4106                         tag: 'span',
4107                         cls: 'sr-only',
4108                         html: 'Toggle navigation'
4109                     },
4110                     {
4111                         tag: 'span',
4112                         cls: 'icon-bar navbar-toggler-icon'
4113                     },
4114                     {
4115                         tag: 'span',
4116                         cls: 'icon-bar'
4117                     },
4118                     {
4119                         tag: 'span',
4120                         cls: 'icon-bar'
4121                     }
4122                 ]
4123             };
4124             
4125             cn.push( Roo.bootstrap.version == 4 ? btn : {
4126                 tag: 'div',
4127                 cls: 'navbar-header',
4128                 cn: [
4129                     btn
4130                 ]
4131             });
4132         }
4133         
4134         cn.push({
4135             tag: 'div',
4136             cls: 'collapse navbar-collapse',
4137             cn : []
4138         });
4139         
4140         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4141         
4142         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4143             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4144             
4145             // tag can override this..
4146             
4147             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4148         }
4149         
4150         if (this.brand !== '') {
4151             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4152             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4153                 tag: 'a',
4154                 href: this.brand_href ? this.brand_href : '#',
4155                 cls: 'navbar-brand',
4156                 cn: [
4157                 this.brand
4158                 ]
4159             });
4160         }
4161         
4162         if(this.main){
4163             cfg.cls += ' main-nav';
4164         }
4165         
4166         
4167         return cfg;
4168
4169         
4170     },
4171     getHeaderChildContainer : function()
4172     {
4173         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4174             return this.el.select('.navbar-header',true).first();
4175         }
4176         
4177         return this.getChildContainer();
4178     },
4179     
4180     
4181     initEvents : function()
4182     {
4183         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4184         
4185         if (this.autohide) {
4186             
4187             var prevScroll = 0;
4188             var ft = this.el;
4189             
4190             Roo.get(document).on('scroll',function(e) {
4191                 var ns = Roo.get(document).getScroll().top;
4192                 var os = prevScroll;
4193                 prevScroll = ns;
4194                 
4195                 if(ns > os){
4196                     ft.removeClass('slideDown');
4197                     ft.addClass('slideUp');
4198                     return;
4199                 }
4200                 ft.removeClass('slideUp');
4201                 ft.addClass('slideDown');
4202                  
4203               
4204           },this);
4205         }
4206     }    
4207     
4208 });
4209
4210
4211
4212  
4213
4214  /*
4215  * - LGPL
4216  *
4217  * navbar
4218  * 
4219  */
4220
4221 /**
4222  * @class Roo.bootstrap.NavSidebar
4223  * @extends Roo.bootstrap.Navbar
4224  * Bootstrap Sidebar class
4225  * 
4226  * @constructor
4227  * Create a new Sidebar
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.NavSidebar = function(config){
4233     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4237     
4238     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4239     
4240     getAutoCreate : function(){
4241         
4242         
4243         return  {
4244             tag: 'div',
4245             cls: 'sidebar sidebar-nav'
4246         };
4247     
4248         
4249     }
4250     
4251     
4252     
4253 });
4254
4255
4256
4257  
4258
4259  /*
4260  * - LGPL
4261  *
4262  * nav group
4263  * 
4264  */
4265
4266 /**
4267  * @class Roo.bootstrap.NavGroup
4268  * @extends Roo.bootstrap.Component
4269  * Bootstrap NavGroup class
4270  * @cfg {String} align (left|right)
4271  * @cfg {Boolean} inverse
4272  * @cfg {String} type (nav|pills|tab) default nav
4273  * @cfg {String} navId - reference Id for navbar.
4274
4275  * 
4276  * @constructor
4277  * Create a new nav group
4278  * @param {Object} config The config object
4279  */
4280
4281 Roo.bootstrap.NavGroup = function(config){
4282     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4283     this.navItems = [];
4284    
4285     Roo.bootstrap.NavGroup.register(this);
4286      this.addEvents({
4287         /**
4288              * @event changed
4289              * Fires when the active item changes
4290              * @param {Roo.bootstrap.NavGroup} this
4291              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4292              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4293          */
4294         'changed': true
4295      });
4296     
4297 };
4298
4299 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4300     
4301     align: '',
4302     inverse: false,
4303     form: false,
4304     type: 'nav',
4305     navId : '',
4306     // private
4307     
4308     navItems : false, 
4309     
4310     getAutoCreate : function()
4311     {
4312         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4313         
4314         cfg = {
4315             tag : 'ul',
4316             cls: 'nav' 
4317         };
4318         
4319         if (['tabs','pills'].indexOf(this.type)!==-1) {
4320             cfg.cls += ' nav-' + this.type
4321         } else {
4322             if (this.type!=='nav') {
4323                 Roo.log('nav type must be nav/tabs/pills')
4324             }
4325             cfg.cls += ' navbar-nav'
4326         }
4327         
4328         if (this.parent() && this.parent().sidebar) {
4329             cfg = {
4330                 tag: 'ul',
4331                 cls: 'dashboard-menu sidebar-menu'
4332             };
4333             
4334             return cfg;
4335         }
4336         
4337         if (this.form === true) {
4338             cfg = {
4339                 tag: 'form',
4340                 cls: 'navbar-form'
4341             };
4342             
4343             if (this.align === 'right') {
4344                 cfg.cls += ' navbar-right ml-md-auto';
4345             } else {
4346                 cfg.cls += ' navbar-left';
4347             }
4348         }
4349         
4350         if (this.align === 'right') {
4351             cfg.cls += ' navbar-right ml-md-auto';
4352         } else {
4353             cfg.cls += ' mr-auto';
4354         }
4355         
4356         if (this.inverse) {
4357             cfg.cls += ' navbar-inverse';
4358             
4359         }
4360         
4361         
4362         return cfg;
4363     },
4364     /**
4365     * sets the active Navigation item
4366     * @param {Roo.bootstrap.NavItem} the new current navitem
4367     */
4368     setActiveItem : function(item)
4369     {
4370         var prev = false;
4371         Roo.each(this.navItems, function(v){
4372             if (v == item) {
4373                 return ;
4374             }
4375             if (v.isActive()) {
4376                 v.setActive(false, true);
4377                 prev = v;
4378                 
4379             }
4380             
4381         });
4382
4383         item.setActive(true, true);
4384         this.fireEvent('changed', this, item, prev);
4385         
4386         
4387     },
4388     /**
4389     * gets the active Navigation item
4390     * @return {Roo.bootstrap.NavItem} the current navitem
4391     */
4392     getActive : function()
4393     {
4394         
4395         var prev = false;
4396         Roo.each(this.navItems, function(v){
4397             
4398             if (v.isActive()) {
4399                 prev = v;
4400                 
4401             }
4402             
4403         });
4404         return prev;
4405     },
4406     
4407     indexOfNav : function()
4408     {
4409         
4410         var prev = false;
4411         Roo.each(this.navItems, function(v,i){
4412             
4413             if (v.isActive()) {
4414                 prev = i;
4415                 
4416             }
4417             
4418         });
4419         return prev;
4420     },
4421     /**
4422     * adds a Navigation item
4423     * @param {Roo.bootstrap.NavItem} the navitem to add
4424     */
4425     addItem : function(cfg)
4426     {
4427         var cn = new Roo.bootstrap.NavItem(cfg);
4428         this.register(cn);
4429         cn.parentId = this.id;
4430         cn.onRender(this.el, null);
4431         return cn;
4432     },
4433     /**
4434     * register a Navigation item
4435     * @param {Roo.bootstrap.NavItem} the navitem to add
4436     */
4437     register : function(item)
4438     {
4439         this.navItems.push( item);
4440         item.navId = this.navId;
4441     
4442     },
4443     
4444     /**
4445     * clear all the Navigation item
4446     */
4447    
4448     clearAll : function()
4449     {
4450         this.navItems = [];
4451         this.el.dom.innerHTML = '';
4452     },
4453     
4454     getNavItem: function(tabId)
4455     {
4456         var ret = false;
4457         Roo.each(this.navItems, function(e) {
4458             if (e.tabId == tabId) {
4459                ret =  e;
4460                return false;
4461             }
4462             return true;
4463             
4464         });
4465         return ret;
4466     },
4467     
4468     setActiveNext : function()
4469     {
4470         var i = this.indexOfNav(this.getActive());
4471         if (i > this.navItems.length) {
4472             return;
4473         }
4474         this.setActiveItem(this.navItems[i+1]);
4475     },
4476     setActivePrev : function()
4477     {
4478         var i = this.indexOfNav(this.getActive());
4479         if (i  < 1) {
4480             return;
4481         }
4482         this.setActiveItem(this.navItems[i-1]);
4483     },
4484     clearWasActive : function(except) {
4485         Roo.each(this.navItems, function(e) {
4486             if (e.tabId != except.tabId && e.was_active) {
4487                e.was_active = false;
4488                return false;
4489             }
4490             return true;
4491             
4492         });
4493     },
4494     getWasActive : function ()
4495     {
4496         var r = false;
4497         Roo.each(this.navItems, function(e) {
4498             if (e.was_active) {
4499                r = e;
4500                return false;
4501             }
4502             return true;
4503             
4504         });
4505         return r;
4506     }
4507     
4508     
4509 });
4510
4511  
4512 Roo.apply(Roo.bootstrap.NavGroup, {
4513     
4514     groups: {},
4515      /**
4516     * register a Navigation Group
4517     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4518     */
4519     register : function(navgrp)
4520     {
4521         this.groups[navgrp.navId] = navgrp;
4522         
4523     },
4524     /**
4525     * fetch a Navigation Group based on the navigation ID
4526     * @param {string} the navgroup to add
4527     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4528     */
4529     get: function(navId) {
4530         if (typeof(this.groups[navId]) == 'undefined') {
4531             return false;
4532             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4533         }
4534         return this.groups[navId] ;
4535     }
4536     
4537     
4538     
4539 });
4540
4541  /*
4542  * - LGPL
4543  *
4544  * row
4545  * 
4546  */
4547
4548 /**
4549  * @class Roo.bootstrap.NavItem
4550  * @extends Roo.bootstrap.Component
4551  * Bootstrap Navbar.NavItem class
4552  * @cfg {String} href  link to
4553  * @cfg {String} html content of button
4554  * @cfg {String} badge text inside badge
4555  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4556  * @cfg {String} glyphicon DEPRICATED - use fa
4557  * @cfg {String} icon DEPRICATED - use fa
4558  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4559  * @cfg {Boolean} active Is item active
4560  * @cfg {Boolean} disabled Is item disabled
4561  
4562  * @cfg {Boolean} preventDefault (true | false) default false
4563  * @cfg {String} tabId the tab that this item activates.
4564  * @cfg {String} tagtype (a|span) render as a href or span?
4565  * @cfg {Boolean} animateRef (true|false) link to element default false  
4566   
4567  * @constructor
4568  * Create a new Navbar Item
4569  * @param {Object} config The config object
4570  */
4571 Roo.bootstrap.NavItem = function(config){
4572     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4573     this.addEvents({
4574         // raw events
4575         /**
4576          * @event click
4577          * The raw click event for the entire grid.
4578          * @param {Roo.EventObject} e
4579          */
4580         "click" : true,
4581          /**
4582             * @event changed
4583             * Fires when the active item active state changes
4584             * @param {Roo.bootstrap.NavItem} this
4585             * @param {boolean} state the new state
4586              
4587          */
4588         'changed': true,
4589         /**
4590             * @event scrollto
4591             * Fires when scroll to element
4592             * @param {Roo.bootstrap.NavItem} this
4593             * @param {Object} options
4594             * @param {Roo.EventObject} e
4595              
4596          */
4597         'scrollto': true
4598     });
4599    
4600 };
4601
4602 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4603     
4604     href: false,
4605     html: '',
4606     badge: '',
4607     icon: false,
4608     fa : false,
4609     glyphicon: false,
4610     active: false,
4611     preventDefault : false,
4612     tabId : false,
4613     tagtype : 'a',
4614     disabled : false,
4615     animateRef : false,
4616     was_active : false,
4617     
4618     getAutoCreate : function(){
4619          
4620         var cfg = {
4621             tag: 'li',
4622             cls: 'nav-item'
4623             
4624         };
4625         
4626         if (this.active) {
4627             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4628         }
4629         if (this.disabled) {
4630             cfg.cls += ' disabled';
4631         }
4632         
4633         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4634             cfg.cn = [
4635                 {
4636                     tag: this.tagtype,
4637                     href : this.href || "#",
4638                     html: this.html || ''
4639                 }
4640             ];
4641             if (this.tagtype == 'a') {
4642                 cfg.cn[0].cls = 'nav-link';
4643             }
4644             if (this.icon) {
4645                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4646             }
4647             if (this.fa) {
4648                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>'
4649             }
4650             if(this.glyphicon) {
4651                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4652             }
4653             
4654             if (this.menu) {
4655                 
4656                 cfg.cn[0].html += " <span class='caret'></span>";
4657              
4658             }
4659             
4660             if (this.badge !== '') {
4661                  
4662                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4663             }
4664         }
4665         
4666         
4667         
4668         return cfg;
4669     },
4670     initEvents: function() 
4671     {
4672         if (typeof (this.menu) != 'undefined') {
4673             this.menu.parentType = this.xtype;
4674             this.menu.triggerEl = this.el;
4675             this.menu = this.addxtype(Roo.apply({}, this.menu));
4676         }
4677         
4678         this.el.select('a',true).on('click', this.onClick, this);
4679         
4680         if(this.tagtype == 'span'){
4681             this.el.select('span',true).on('click', this.onClick, this);
4682         }
4683        
4684         // at this point parent should be available..
4685         this.parent().register(this);
4686     },
4687     
4688     onClick : function(e)
4689     {
4690         if (e.getTarget('.dropdown-menu-item')) {
4691             // did you click on a menu itemm.... - then don't trigger onclick..
4692             return;
4693         }
4694         
4695         if(
4696                 this.preventDefault || 
4697                 this.href == '#' 
4698         ){
4699             Roo.log("NavItem - prevent Default?");
4700             e.preventDefault();
4701         }
4702         
4703         if (this.disabled) {
4704             return;
4705         }
4706         
4707         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4708         if (tg && tg.transition) {
4709             Roo.log("waiting for the transitionend");
4710             return;
4711         }
4712         
4713         
4714         
4715         //Roo.log("fire event clicked");
4716         if(this.fireEvent('click', this, e) === false){
4717             return;
4718         };
4719         
4720         if(this.tagtype == 'span'){
4721             return;
4722         }
4723         
4724         //Roo.log(this.href);
4725         var ael = this.el.select('a',true).first();
4726         //Roo.log(ael);
4727         
4728         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4729             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4730             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4731                 return; // ignore... - it's a 'hash' to another page.
4732             }
4733             Roo.log("NavItem - prevent Default?");
4734             e.preventDefault();
4735             this.scrollToElement(e);
4736         }
4737         
4738         
4739         var p =  this.parent();
4740    
4741         if (['tabs','pills'].indexOf(p.type)!==-1) {
4742             if (typeof(p.setActiveItem) !== 'undefined') {
4743                 p.setActiveItem(this);
4744             }
4745         }
4746         
4747         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4748         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4749             // remove the collapsed menu expand...
4750             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4751         }
4752     },
4753     
4754     isActive: function () {
4755         return this.active
4756     },
4757     setActive : function(state, fire, is_was_active)
4758     {
4759         if (this.active && !state && this.navId) {
4760             this.was_active = true;
4761             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4762             if (nv) {
4763                 nv.clearWasActive(this);
4764             }
4765             
4766         }
4767         this.active = state;
4768         
4769         if (!state ) {
4770             this.el.removeClass('active');
4771         } else if (!this.el.hasClass('active')) {
4772             this.el.addClass('active');
4773         }
4774         if (fire) {
4775             this.fireEvent('changed', this, state);
4776         }
4777         
4778         // show a panel if it's registered and related..
4779         
4780         if (!this.navId || !this.tabId || !state || is_was_active) {
4781             return;
4782         }
4783         
4784         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4785         if (!tg) {
4786             return;
4787         }
4788         var pan = tg.getPanelByName(this.tabId);
4789         if (!pan) {
4790             return;
4791         }
4792         // if we can not flip to new panel - go back to old nav highlight..
4793         if (false == tg.showPanel(pan)) {
4794             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4795             if (nv) {
4796                 var onav = nv.getWasActive();
4797                 if (onav) {
4798                     onav.setActive(true, false, true);
4799                 }
4800             }
4801             
4802         }
4803         
4804         
4805         
4806     },
4807      // this should not be here...
4808     setDisabled : function(state)
4809     {
4810         this.disabled = state;
4811         if (!state ) {
4812             this.el.removeClass('disabled');
4813         } else if (!this.el.hasClass('disabled')) {
4814             this.el.addClass('disabled');
4815         }
4816         
4817     },
4818     
4819     /**
4820      * Fetch the element to display the tooltip on.
4821      * @return {Roo.Element} defaults to this.el
4822      */
4823     tooltipEl : function()
4824     {
4825         return this.el.select('' + this.tagtype + '', true).first();
4826     },
4827     
4828     scrollToElement : function(e)
4829     {
4830         var c = document.body;
4831         
4832         /*
4833          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4834          */
4835         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4836             c = document.documentElement;
4837         }
4838         
4839         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4840         
4841         if(!target){
4842             return;
4843         }
4844
4845         var o = target.calcOffsetsTo(c);
4846         
4847         var options = {
4848             target : target,
4849             value : o[1]
4850         };
4851         
4852         this.fireEvent('scrollto', this, options, e);
4853         
4854         Roo.get(c).scrollTo('top', options.value, true);
4855         
4856         return;
4857     }
4858 });
4859  
4860
4861  /*
4862  * - LGPL
4863  *
4864  * sidebar item
4865  *
4866  *  li
4867  *    <span> icon </span>
4868  *    <span> text </span>
4869  *    <span>badge </span>
4870  */
4871
4872 /**
4873  * @class Roo.bootstrap.NavSidebarItem
4874  * @extends Roo.bootstrap.NavItem
4875  * Bootstrap Navbar.NavSidebarItem class
4876  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4877  * {Boolean} open is the menu open
4878  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4879  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4880  * {String} buttonSize (sm|md|lg)the extra classes for the button
4881  * {Boolean} showArrow show arrow next to the text (default true)
4882  * @constructor
4883  * Create a new Navbar Button
4884  * @param {Object} config The config object
4885  */
4886 Roo.bootstrap.NavSidebarItem = function(config){
4887     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4888     this.addEvents({
4889         // raw events
4890         /**
4891          * @event click
4892          * The raw click event for the entire grid.
4893          * @param {Roo.EventObject} e
4894          */
4895         "click" : true,
4896          /**
4897             * @event changed
4898             * Fires when the active item active state changes
4899             * @param {Roo.bootstrap.NavSidebarItem} this
4900             * @param {boolean} state the new state
4901              
4902          */
4903         'changed': true
4904     });
4905    
4906 };
4907
4908 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4909     
4910     badgeWeight : 'default',
4911     
4912     open: false,
4913     
4914     buttonView : false,
4915     
4916     buttonWeight : 'default',
4917     
4918     buttonSize : 'md',
4919     
4920     showArrow : true,
4921     
4922     getAutoCreate : function(){
4923         
4924         
4925         var a = {
4926                 tag: 'a',
4927                 href : this.href || '#',
4928                 cls: '',
4929                 html : '',
4930                 cn : []
4931         };
4932         
4933         if(this.buttonView){
4934             a = {
4935                 tag: 'button',
4936                 href : this.href || '#',
4937                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4938                 html : this.html,
4939                 cn : []
4940             };
4941         }
4942         
4943         var cfg = {
4944             tag: 'li',
4945             cls: '',
4946             cn: [ a ]
4947         };
4948         
4949         if (this.active) {
4950             cfg.cls += ' active';
4951         }
4952         
4953         if (this.disabled) {
4954             cfg.cls += ' disabled';
4955         }
4956         if (this.open) {
4957             cfg.cls += ' open x-open';
4958         }
4959         // left icon..
4960         if (this.glyphicon || this.icon) {
4961             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4962             a.cn.push({ tag : 'i', cls : c }) ;
4963         }
4964         
4965         if(!this.buttonView){
4966             var span = {
4967                 tag: 'span',
4968                 html : this.html || ''
4969             };
4970
4971             a.cn.push(span);
4972             
4973         }
4974         
4975         if (this.badge !== '') {
4976             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4977         }
4978         
4979         if (this.menu) {
4980             
4981             if(this.showArrow){
4982                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4983             }
4984             
4985             a.cls += ' dropdown-toggle treeview' ;
4986         }
4987         
4988         return cfg;
4989     },
4990     
4991     initEvents : function()
4992     { 
4993         if (typeof (this.menu) != 'undefined') {
4994             this.menu.parentType = this.xtype;
4995             this.menu.triggerEl = this.el;
4996             this.menu = this.addxtype(Roo.apply({}, this.menu));
4997         }
4998         
4999         this.el.on('click', this.onClick, this);
5000         
5001         if(this.badge !== ''){
5002             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5003         }
5004         
5005     },
5006     
5007     onClick : function(e)
5008     {
5009         if(this.disabled){
5010             e.preventDefault();
5011             return;
5012         }
5013         
5014         if(this.preventDefault){
5015             e.preventDefault();
5016         }
5017         
5018         this.fireEvent('click', this);
5019     },
5020     
5021     disable : function()
5022     {
5023         this.setDisabled(true);
5024     },
5025     
5026     enable : function()
5027     {
5028         this.setDisabled(false);
5029     },
5030     
5031     setDisabled : function(state)
5032     {
5033         if(this.disabled == state){
5034             return;
5035         }
5036         
5037         this.disabled = state;
5038         
5039         if (state) {
5040             this.el.addClass('disabled');
5041             return;
5042         }
5043         
5044         this.el.removeClass('disabled');
5045         
5046         return;
5047     },
5048     
5049     setActive : function(state)
5050     {
5051         if(this.active == state){
5052             return;
5053         }
5054         
5055         this.active = state;
5056         
5057         if (state) {
5058             this.el.addClass('active');
5059             return;
5060         }
5061         
5062         this.el.removeClass('active');
5063         
5064         return;
5065     },
5066     
5067     isActive: function () 
5068     {
5069         return this.active;
5070     },
5071     
5072     setBadge : function(str)
5073     {
5074         if(!this.badgeEl){
5075             return;
5076         }
5077         
5078         this.badgeEl.dom.innerHTML = str;
5079     }
5080     
5081    
5082      
5083  
5084 });
5085  
5086
5087  /*
5088  * - LGPL
5089  *
5090  * row
5091  * 
5092  */
5093
5094 /**
5095  * @class Roo.bootstrap.Row
5096  * @extends Roo.bootstrap.Component
5097  * Bootstrap Row class (contains columns...)
5098  * 
5099  * @constructor
5100  * Create a new Row
5101  * @param {Object} config The config object
5102  */
5103
5104 Roo.bootstrap.Row = function(config){
5105     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5106 };
5107
5108 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5109     
5110     getAutoCreate : function(){
5111        return {
5112             cls: 'row clearfix'
5113        };
5114     }
5115     
5116     
5117 });
5118
5119  
5120
5121  /*
5122  * - LGPL
5123  *
5124  * element
5125  * 
5126  */
5127
5128 /**
5129  * @class Roo.bootstrap.Element
5130  * @extends Roo.bootstrap.Component
5131  * Bootstrap Element class
5132  * @cfg {String} html contents of the element
5133  * @cfg {String} tag tag of the element
5134  * @cfg {String} cls class of the element
5135  * @cfg {Boolean} preventDefault (true|false) default false
5136  * @cfg {Boolean} clickable (true|false) default false
5137  * 
5138  * @constructor
5139  * Create a new Element
5140  * @param {Object} config The config object
5141  */
5142
5143 Roo.bootstrap.Element = function(config){
5144     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5145     
5146     this.addEvents({
5147         // raw events
5148         /**
5149          * @event click
5150          * When a element is chick
5151          * @param {Roo.bootstrap.Element} this
5152          * @param {Roo.EventObject} e
5153          */
5154         "click" : true
5155     });
5156 };
5157
5158 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5159     
5160     tag: 'div',
5161     cls: '',
5162     html: '',
5163     preventDefault: false, 
5164     clickable: false,
5165     
5166     getAutoCreate : function(){
5167         
5168         var cfg = {
5169             tag: this.tag,
5170             // cls: this.cls, double assign in parent class Component.js :: onRender
5171             html: this.html
5172         };
5173         
5174         return cfg;
5175     },
5176     
5177     initEvents: function() 
5178     {
5179         Roo.bootstrap.Element.superclass.initEvents.call(this);
5180         
5181         if(this.clickable){
5182             this.el.on('click', this.onClick, this);
5183         }
5184         
5185     },
5186     
5187     onClick : function(e)
5188     {
5189         if(this.preventDefault){
5190             e.preventDefault();
5191         }
5192         
5193         this.fireEvent('click', this, e);
5194     },
5195     
5196     getValue : function()
5197     {
5198         return this.el.dom.innerHTML;
5199     },
5200     
5201     setValue : function(value)
5202     {
5203         this.el.dom.innerHTML = value;
5204     }
5205    
5206 });
5207
5208  
5209
5210  /*
5211  * - LGPL
5212  *
5213  * pagination
5214  * 
5215  */
5216
5217 /**
5218  * @class Roo.bootstrap.Pagination
5219  * @extends Roo.bootstrap.Component
5220  * Bootstrap Pagination class
5221  * @cfg {String} size xs | sm | md | lg
5222  * @cfg {Boolean} inverse false | true
5223  * 
5224  * @constructor
5225  * Create a new Pagination
5226  * @param {Object} config The config object
5227  */
5228
5229 Roo.bootstrap.Pagination = function(config){
5230     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5231 };
5232
5233 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5234     
5235     cls: false,
5236     size: false,
5237     inverse: false,
5238     
5239     getAutoCreate : function(){
5240         var cfg = {
5241             tag: 'ul',
5242                 cls: 'pagination'
5243         };
5244         if (this.inverse) {
5245             cfg.cls += ' inverse';
5246         }
5247         if (this.html) {
5248             cfg.html=this.html;
5249         }
5250         if (this.cls) {
5251             cfg.cls += " " + this.cls;
5252         }
5253         return cfg;
5254     }
5255    
5256 });
5257
5258  
5259
5260  /*
5261  * - LGPL
5262  *
5263  * Pagination item
5264  * 
5265  */
5266
5267
5268 /**
5269  * @class Roo.bootstrap.PaginationItem
5270  * @extends Roo.bootstrap.Component
5271  * Bootstrap PaginationItem class
5272  * @cfg {String} html text
5273  * @cfg {String} href the link
5274  * @cfg {Boolean} preventDefault (true | false) default true
5275  * @cfg {Boolean} active (true | false) default false
5276  * @cfg {Boolean} disabled default false
5277  * 
5278  * 
5279  * @constructor
5280  * Create a new PaginationItem
5281  * @param {Object} config The config object
5282  */
5283
5284
5285 Roo.bootstrap.PaginationItem = function(config){
5286     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5287     this.addEvents({
5288         // raw events
5289         /**
5290          * @event click
5291          * The raw click event for the entire grid.
5292          * @param {Roo.EventObject} e
5293          */
5294         "click" : true
5295     });
5296 };
5297
5298 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5299     
5300     href : false,
5301     html : false,
5302     preventDefault: true,
5303     active : false,
5304     cls : false,
5305     disabled: false,
5306     
5307     getAutoCreate : function(){
5308         var cfg= {
5309             tag: 'li',
5310             cn: [
5311                 {
5312                     tag : 'a',
5313                     href : this.href ? this.href : '#',
5314                     html : this.html ? this.html : ''
5315                 }
5316             ]
5317         };
5318         
5319         if(this.cls){
5320             cfg.cls = this.cls;
5321         }
5322         
5323         if(this.disabled){
5324             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5325         }
5326         
5327         if(this.active){
5328             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5329         }
5330         
5331         return cfg;
5332     },
5333     
5334     initEvents: function() {
5335         
5336         this.el.on('click', this.onClick, this);
5337         
5338     },
5339     onClick : function(e)
5340     {
5341         Roo.log('PaginationItem on click ');
5342         if(this.preventDefault){
5343             e.preventDefault();
5344         }
5345         
5346         if(this.disabled){
5347             return;
5348         }
5349         
5350         this.fireEvent('click', this, e);
5351     }
5352    
5353 });
5354
5355  
5356
5357  /*
5358  * - LGPL
5359  *
5360  * slider
5361  * 
5362  */
5363
5364
5365 /**
5366  * @class Roo.bootstrap.Slider
5367  * @extends Roo.bootstrap.Component
5368  * Bootstrap Slider class
5369  *    
5370  * @constructor
5371  * Create a new Slider
5372  * @param {Object} config The config object
5373  */
5374
5375 Roo.bootstrap.Slider = function(config){
5376     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5377 };
5378
5379 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5380     
5381     getAutoCreate : function(){
5382         
5383         var cfg = {
5384             tag: 'div',
5385             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5386             cn: [
5387                 {
5388                     tag: 'a',
5389                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5390                 }
5391             ]
5392         };
5393         
5394         return cfg;
5395     }
5396    
5397 });
5398
5399  /*
5400  * Based on:
5401  * Ext JS Library 1.1.1
5402  * Copyright(c) 2006-2007, Ext JS, LLC.
5403  *
5404  * Originally Released Under LGPL - original licence link has changed is not relivant.
5405  *
5406  * Fork - LGPL
5407  * <script type="text/javascript">
5408  */
5409  
5410
5411 /**
5412  * @class Roo.grid.ColumnModel
5413  * @extends Roo.util.Observable
5414  * This is the default implementation of a ColumnModel used by the Grid. It defines
5415  * the columns in the grid.
5416  * <br>Usage:<br>
5417  <pre><code>
5418  var colModel = new Roo.grid.ColumnModel([
5419         {header: "Ticker", width: 60, sortable: true, locked: true},
5420         {header: "Company Name", width: 150, sortable: true},
5421         {header: "Market Cap.", width: 100, sortable: true},
5422         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5423         {header: "Employees", width: 100, sortable: true, resizable: false}
5424  ]);
5425  </code></pre>
5426  * <p>
5427  
5428  * The config options listed for this class are options which may appear in each
5429  * individual column definition.
5430  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5431  * @constructor
5432  * @param {Object} config An Array of column config objects. See this class's
5433  * config objects for details.
5434 */
5435 Roo.grid.ColumnModel = function(config){
5436         /**
5437      * The config passed into the constructor
5438      */
5439     this.config = config;
5440     this.lookup = {};
5441
5442     // if no id, create one
5443     // if the column does not have a dataIndex mapping,
5444     // map it to the order it is in the config
5445     for(var i = 0, len = config.length; i < len; i++){
5446         var c = config[i];
5447         if(typeof c.dataIndex == "undefined"){
5448             c.dataIndex = i;
5449         }
5450         if(typeof c.renderer == "string"){
5451             c.renderer = Roo.util.Format[c.renderer];
5452         }
5453         if(typeof c.id == "undefined"){
5454             c.id = Roo.id();
5455         }
5456         if(c.editor && c.editor.xtype){
5457             c.editor  = Roo.factory(c.editor, Roo.grid);
5458         }
5459         if(c.editor && c.editor.isFormField){
5460             c.editor = new Roo.grid.GridEditor(c.editor);
5461         }
5462         this.lookup[c.id] = c;
5463     }
5464
5465     /**
5466      * The width of columns which have no width specified (defaults to 100)
5467      * @type Number
5468      */
5469     this.defaultWidth = 100;
5470
5471     /**
5472      * Default sortable of columns which have no sortable specified (defaults to false)
5473      * @type Boolean
5474      */
5475     this.defaultSortable = false;
5476
5477     this.addEvents({
5478         /**
5479              * @event widthchange
5480              * Fires when the width of a column changes.
5481              * @param {ColumnModel} this
5482              * @param {Number} columnIndex The column index
5483              * @param {Number} newWidth The new width
5484              */
5485             "widthchange": true,
5486         /**
5487              * @event headerchange
5488              * Fires when the text of a header changes.
5489              * @param {ColumnModel} this
5490              * @param {Number} columnIndex The column index
5491              * @param {Number} newText The new header text
5492              */
5493             "headerchange": true,
5494         /**
5495              * @event hiddenchange
5496              * Fires when a column is hidden or "unhidden".
5497              * @param {ColumnModel} this
5498              * @param {Number} columnIndex The column index
5499              * @param {Boolean} hidden true if hidden, false otherwise
5500              */
5501             "hiddenchange": true,
5502             /**
5503          * @event columnmoved
5504          * Fires when a column is moved.
5505          * @param {ColumnModel} this
5506          * @param {Number} oldIndex
5507          * @param {Number} newIndex
5508          */
5509         "columnmoved" : true,
5510         /**
5511          * @event columlockchange
5512          * Fires when a column's locked state is changed
5513          * @param {ColumnModel} this
5514          * @param {Number} colIndex
5515          * @param {Boolean} locked true if locked
5516          */
5517         "columnlockchange" : true
5518     });
5519     Roo.grid.ColumnModel.superclass.constructor.call(this);
5520 };
5521 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5522     /**
5523      * @cfg {String} header The header text to display in the Grid view.
5524      */
5525     /**
5526      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5527      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5528      * specified, the column's index is used as an index into the Record's data Array.
5529      */
5530     /**
5531      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5532      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5533      */
5534     /**
5535      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5536      * Defaults to the value of the {@link #defaultSortable} property.
5537      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5538      */
5539     /**
5540      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5541      */
5542     /**
5543      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5544      */
5545     /**
5546      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5547      */
5548     /**
5549      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5550      */
5551     /**
5552      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5553      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5554      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5555      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5556      */
5557        /**
5558      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5559      */
5560     /**
5561      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5562      */
5563     /**
5564      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5565      */
5566     /**
5567      * @cfg {String} cursor (Optional)
5568      */
5569     /**
5570      * @cfg {String} tooltip (Optional)
5571      */
5572     /**
5573      * @cfg {Number} xs (Optional)
5574      */
5575     /**
5576      * @cfg {Number} sm (Optional)
5577      */
5578     /**
5579      * @cfg {Number} md (Optional)
5580      */
5581     /**
5582      * @cfg {Number} lg (Optional)
5583      */
5584     /**
5585      * Returns the id of the column at the specified index.
5586      * @param {Number} index The column index
5587      * @return {String} the id
5588      */
5589     getColumnId : function(index){
5590         return this.config[index].id;
5591     },
5592
5593     /**
5594      * Returns the column for a specified id.
5595      * @param {String} id The column id
5596      * @return {Object} the column
5597      */
5598     getColumnById : function(id){
5599         return this.lookup[id];
5600     },
5601
5602     
5603     /**
5604      * Returns the column for a specified dataIndex.
5605      * @param {String} dataIndex The column dataIndex
5606      * @return {Object|Boolean} the column or false if not found
5607      */
5608     getColumnByDataIndex: function(dataIndex){
5609         var index = this.findColumnIndex(dataIndex);
5610         return index > -1 ? this.config[index] : false;
5611     },
5612     
5613     /**
5614      * Returns the index for a specified column id.
5615      * @param {String} id The column id
5616      * @return {Number} the index, or -1 if not found
5617      */
5618     getIndexById : function(id){
5619         for(var i = 0, len = this.config.length; i < len; i++){
5620             if(this.config[i].id == id){
5621                 return i;
5622             }
5623         }
5624         return -1;
5625     },
5626     
5627     /**
5628      * Returns the index for a specified column dataIndex.
5629      * @param {String} dataIndex The column dataIndex
5630      * @return {Number} the index, or -1 if not found
5631      */
5632     
5633     findColumnIndex : function(dataIndex){
5634         for(var i = 0, len = this.config.length; i < len; i++){
5635             if(this.config[i].dataIndex == dataIndex){
5636                 return i;
5637             }
5638         }
5639         return -1;
5640     },
5641     
5642     
5643     moveColumn : function(oldIndex, newIndex){
5644         var c = this.config[oldIndex];
5645         this.config.splice(oldIndex, 1);
5646         this.config.splice(newIndex, 0, c);
5647         this.dataMap = null;
5648         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5649     },
5650
5651     isLocked : function(colIndex){
5652         return this.config[colIndex].locked === true;
5653     },
5654
5655     setLocked : function(colIndex, value, suppressEvent){
5656         if(this.isLocked(colIndex) == value){
5657             return;
5658         }
5659         this.config[colIndex].locked = value;
5660         if(!suppressEvent){
5661             this.fireEvent("columnlockchange", this, colIndex, value);
5662         }
5663     },
5664
5665     getTotalLockedWidth : function(){
5666         var totalWidth = 0;
5667         for(var i = 0; i < this.config.length; i++){
5668             if(this.isLocked(i) && !this.isHidden(i)){
5669                 this.totalWidth += this.getColumnWidth(i);
5670             }
5671         }
5672         return totalWidth;
5673     },
5674
5675     getLockedCount : function(){
5676         for(var i = 0, len = this.config.length; i < len; i++){
5677             if(!this.isLocked(i)){
5678                 return i;
5679             }
5680         }
5681         
5682         return this.config.length;
5683     },
5684
5685     /**
5686      * Returns the number of columns.
5687      * @return {Number}
5688      */
5689     getColumnCount : function(visibleOnly){
5690         if(visibleOnly === true){
5691             var c = 0;
5692             for(var i = 0, len = this.config.length; i < len; i++){
5693                 if(!this.isHidden(i)){
5694                     c++;
5695                 }
5696             }
5697             return c;
5698         }
5699         return this.config.length;
5700     },
5701
5702     /**
5703      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5704      * @param {Function} fn
5705      * @param {Object} scope (optional)
5706      * @return {Array} result
5707      */
5708     getColumnsBy : function(fn, scope){
5709         var r = [];
5710         for(var i = 0, len = this.config.length; i < len; i++){
5711             var c = this.config[i];
5712             if(fn.call(scope||this, c, i) === true){
5713                 r[r.length] = c;
5714             }
5715         }
5716         return r;
5717     },
5718
5719     /**
5720      * Returns true if the specified column is sortable.
5721      * @param {Number} col The column index
5722      * @return {Boolean}
5723      */
5724     isSortable : function(col){
5725         if(typeof this.config[col].sortable == "undefined"){
5726             return this.defaultSortable;
5727         }
5728         return this.config[col].sortable;
5729     },
5730
5731     /**
5732      * Returns the rendering (formatting) function defined for the column.
5733      * @param {Number} col The column index.
5734      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5735      */
5736     getRenderer : function(col){
5737         if(!this.config[col].renderer){
5738             return Roo.grid.ColumnModel.defaultRenderer;
5739         }
5740         return this.config[col].renderer;
5741     },
5742
5743     /**
5744      * Sets the rendering (formatting) function for a column.
5745      * @param {Number} col The column index
5746      * @param {Function} fn The function to use to process the cell's raw data
5747      * to return HTML markup for the grid view. The render function is called with
5748      * the following parameters:<ul>
5749      * <li>Data value.</li>
5750      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5751      * <li>css A CSS style string to apply to the table cell.</li>
5752      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5753      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5754      * <li>Row index</li>
5755      * <li>Column index</li>
5756      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5757      */
5758     setRenderer : function(col, fn){
5759         this.config[col].renderer = fn;
5760     },
5761
5762     /**
5763      * Returns the width for the specified column.
5764      * @param {Number} col The column index
5765      * @return {Number}
5766      */
5767     getColumnWidth : function(col){
5768         return this.config[col].width * 1 || this.defaultWidth;
5769     },
5770
5771     /**
5772      * Sets the width for a column.
5773      * @param {Number} col The column index
5774      * @param {Number} width The new width
5775      */
5776     setColumnWidth : function(col, width, suppressEvent){
5777         this.config[col].width = width;
5778         this.totalWidth = null;
5779         if(!suppressEvent){
5780              this.fireEvent("widthchange", this, col, width);
5781         }
5782     },
5783
5784     /**
5785      * Returns the total width of all columns.
5786      * @param {Boolean} includeHidden True to include hidden column widths
5787      * @return {Number}
5788      */
5789     getTotalWidth : function(includeHidden){
5790         if(!this.totalWidth){
5791             this.totalWidth = 0;
5792             for(var i = 0, len = this.config.length; i < len; i++){
5793                 if(includeHidden || !this.isHidden(i)){
5794                     this.totalWidth += this.getColumnWidth(i);
5795                 }
5796             }
5797         }
5798         return this.totalWidth;
5799     },
5800
5801     /**
5802      * Returns the header for the specified column.
5803      * @param {Number} col The column index
5804      * @return {String}
5805      */
5806     getColumnHeader : function(col){
5807         return this.config[col].header;
5808     },
5809
5810     /**
5811      * Sets the header for a column.
5812      * @param {Number} col The column index
5813      * @param {String} header The new header
5814      */
5815     setColumnHeader : function(col, header){
5816         this.config[col].header = header;
5817         this.fireEvent("headerchange", this, col, header);
5818     },
5819
5820     /**
5821      * Returns the tooltip for the specified column.
5822      * @param {Number} col The column index
5823      * @return {String}
5824      */
5825     getColumnTooltip : function(col){
5826             return this.config[col].tooltip;
5827     },
5828     /**
5829      * Sets the tooltip for a column.
5830      * @param {Number} col The column index
5831      * @param {String} tooltip The new tooltip
5832      */
5833     setColumnTooltip : function(col, tooltip){
5834             this.config[col].tooltip = tooltip;
5835     },
5836
5837     /**
5838      * Returns the dataIndex for the specified column.
5839      * @param {Number} col The column index
5840      * @return {Number}
5841      */
5842     getDataIndex : function(col){
5843         return this.config[col].dataIndex;
5844     },
5845
5846     /**
5847      * Sets the dataIndex for a column.
5848      * @param {Number} col The column index
5849      * @param {Number} dataIndex The new dataIndex
5850      */
5851     setDataIndex : function(col, dataIndex){
5852         this.config[col].dataIndex = dataIndex;
5853     },
5854
5855     
5856     
5857     /**
5858      * Returns true if the cell is editable.
5859      * @param {Number} colIndex The column index
5860      * @param {Number} rowIndex The row index - this is nto actually used..?
5861      * @return {Boolean}
5862      */
5863     isCellEditable : function(colIndex, rowIndex){
5864         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5865     },
5866
5867     /**
5868      * Returns the editor defined for the cell/column.
5869      * return false or null to disable editing.
5870      * @param {Number} colIndex The column index
5871      * @param {Number} rowIndex The row index
5872      * @return {Object}
5873      */
5874     getCellEditor : function(colIndex, rowIndex){
5875         return this.config[colIndex].editor;
5876     },
5877
5878     /**
5879      * Sets if a column is editable.
5880      * @param {Number} col The column index
5881      * @param {Boolean} editable True if the column is editable
5882      */
5883     setEditable : function(col, editable){
5884         this.config[col].editable = editable;
5885     },
5886
5887
5888     /**
5889      * Returns true if the column is hidden.
5890      * @param {Number} colIndex The column index
5891      * @return {Boolean}
5892      */
5893     isHidden : function(colIndex){
5894         return this.config[colIndex].hidden;
5895     },
5896
5897
5898     /**
5899      * Returns true if the column width cannot be changed
5900      */
5901     isFixed : function(colIndex){
5902         return this.config[colIndex].fixed;
5903     },
5904
5905     /**
5906      * Returns true if the column can be resized
5907      * @return {Boolean}
5908      */
5909     isResizable : function(colIndex){
5910         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5911     },
5912     /**
5913      * Sets if a column is hidden.
5914      * @param {Number} colIndex The column index
5915      * @param {Boolean} hidden True if the column is hidden
5916      */
5917     setHidden : function(colIndex, hidden){
5918         this.config[colIndex].hidden = hidden;
5919         this.totalWidth = null;
5920         this.fireEvent("hiddenchange", this, colIndex, hidden);
5921     },
5922
5923     /**
5924      * Sets the editor for a column.
5925      * @param {Number} col The column index
5926      * @param {Object} editor The editor object
5927      */
5928     setEditor : function(col, editor){
5929         this.config[col].editor = editor;
5930     }
5931 });
5932
5933 Roo.grid.ColumnModel.defaultRenderer = function(value)
5934 {
5935     if(typeof value == "object") {
5936         return value;
5937     }
5938         if(typeof value == "string" && value.length < 1){
5939             return "&#160;";
5940         }
5941     
5942         return String.format("{0}", value);
5943 };
5944
5945 // Alias for backwards compatibility
5946 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5947 /*
5948  * Based on:
5949  * Ext JS Library 1.1.1
5950  * Copyright(c) 2006-2007, Ext JS, LLC.
5951  *
5952  * Originally Released Under LGPL - original licence link has changed is not relivant.
5953  *
5954  * Fork - LGPL
5955  * <script type="text/javascript">
5956  */
5957  
5958 /**
5959  * @class Roo.LoadMask
5960  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5961  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5962  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5963  * element's UpdateManager load indicator and will be destroyed after the initial load.
5964  * @constructor
5965  * Create a new LoadMask
5966  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5967  * @param {Object} config The config object
5968  */
5969 Roo.LoadMask = function(el, config){
5970     this.el = Roo.get(el);
5971     Roo.apply(this, config);
5972     if(this.store){
5973         this.store.on('beforeload', this.onBeforeLoad, this);
5974         this.store.on('load', this.onLoad, this);
5975         this.store.on('loadexception', this.onLoadException, this);
5976         this.removeMask = false;
5977     }else{
5978         var um = this.el.getUpdateManager();
5979         um.showLoadIndicator = false; // disable the default indicator
5980         um.on('beforeupdate', this.onBeforeLoad, this);
5981         um.on('update', this.onLoad, this);
5982         um.on('failure', this.onLoad, this);
5983         this.removeMask = true;
5984     }
5985 };
5986
5987 Roo.LoadMask.prototype = {
5988     /**
5989      * @cfg {Boolean} removeMask
5990      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5991      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5992      */
5993     /**
5994      * @cfg {String} msg
5995      * The text to display in a centered loading message box (defaults to 'Loading...')
5996      */
5997     msg : 'Loading...',
5998     /**
5999      * @cfg {String} msgCls
6000      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6001      */
6002     msgCls : 'x-mask-loading',
6003
6004     /**
6005      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6006      * @type Boolean
6007      */
6008     disabled: false,
6009
6010     /**
6011      * Disables the mask to prevent it from being displayed
6012      */
6013     disable : function(){
6014        this.disabled = true;
6015     },
6016
6017     /**
6018      * Enables the mask so that it can be displayed
6019      */
6020     enable : function(){
6021         this.disabled = false;
6022     },
6023     
6024     onLoadException : function()
6025     {
6026         Roo.log(arguments);
6027         
6028         if (typeof(arguments[3]) != 'undefined') {
6029             Roo.MessageBox.alert("Error loading",arguments[3]);
6030         } 
6031         /*
6032         try {
6033             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6034                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6035             }   
6036         } catch(e) {
6037             
6038         }
6039         */
6040     
6041         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6042     },
6043     // private
6044     onLoad : function()
6045     {
6046         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6047     },
6048
6049     // private
6050     onBeforeLoad : function(){
6051         if(!this.disabled){
6052             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6053         }
6054     },
6055
6056     // private
6057     destroy : function(){
6058         if(this.store){
6059             this.store.un('beforeload', this.onBeforeLoad, this);
6060             this.store.un('load', this.onLoad, this);
6061             this.store.un('loadexception', this.onLoadException, this);
6062         }else{
6063             var um = this.el.getUpdateManager();
6064             um.un('beforeupdate', this.onBeforeLoad, this);
6065             um.un('update', this.onLoad, this);
6066             um.un('failure', this.onLoad, this);
6067         }
6068     }
6069 };/*
6070  * - LGPL
6071  *
6072  * table
6073  * 
6074  */
6075
6076 /**
6077  * @class Roo.bootstrap.Table
6078  * @extends Roo.bootstrap.Component
6079  * Bootstrap Table class
6080  * @cfg {String} cls table class
6081  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6082  * @cfg {String} bgcolor Specifies the background color for a table
6083  * @cfg {Number} border Specifies whether the table cells should have borders or not
6084  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6085  * @cfg {Number} cellspacing Specifies the space between cells
6086  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6087  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6088  * @cfg {String} sortable Specifies that the table should be sortable
6089  * @cfg {String} summary Specifies a summary of the content of a table
6090  * @cfg {Number} width Specifies the width of a table
6091  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6092  * 
6093  * @cfg {boolean} striped Should the rows be alternative striped
6094  * @cfg {boolean} bordered Add borders to the table
6095  * @cfg {boolean} hover Add hover highlighting
6096  * @cfg {boolean} condensed Format condensed
6097  * @cfg {boolean} responsive Format condensed
6098  * @cfg {Boolean} loadMask (true|false) default false
6099  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6100  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6101  * @cfg {Boolean} rowSelection (true|false) default false
6102  * @cfg {Boolean} cellSelection (true|false) default false
6103  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6104  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6105  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6106  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6107  
6108  * 
6109  * @constructor
6110  * Create a new Table
6111  * @param {Object} config The config object
6112  */
6113
6114 Roo.bootstrap.Table = function(config){
6115     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6116     
6117   
6118     
6119     // BC...
6120     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6121     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6122     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6123     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6124     
6125     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6126     if (this.sm) {
6127         this.sm.grid = this;
6128         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6129         this.sm = this.selModel;
6130         this.sm.xmodule = this.xmodule || false;
6131     }
6132     
6133     if (this.cm && typeof(this.cm.config) == 'undefined') {
6134         this.colModel = new Roo.grid.ColumnModel(this.cm);
6135         this.cm = this.colModel;
6136         this.cm.xmodule = this.xmodule || false;
6137     }
6138     if (this.store) {
6139         this.store= Roo.factory(this.store, Roo.data);
6140         this.ds = this.store;
6141         this.ds.xmodule = this.xmodule || false;
6142          
6143     }
6144     if (this.footer && this.store) {
6145         this.footer.dataSource = this.ds;
6146         this.footer = Roo.factory(this.footer);
6147     }
6148     
6149     /** @private */
6150     this.addEvents({
6151         /**
6152          * @event cellclick
6153          * Fires when a cell is clicked
6154          * @param {Roo.bootstrap.Table} this
6155          * @param {Roo.Element} el
6156          * @param {Number} rowIndex
6157          * @param {Number} columnIndex
6158          * @param {Roo.EventObject} e
6159          */
6160         "cellclick" : true,
6161         /**
6162          * @event celldblclick
6163          * Fires when a cell is double clicked
6164          * @param {Roo.bootstrap.Table} this
6165          * @param {Roo.Element} el
6166          * @param {Number} rowIndex
6167          * @param {Number} columnIndex
6168          * @param {Roo.EventObject} e
6169          */
6170         "celldblclick" : true,
6171         /**
6172          * @event rowclick
6173          * Fires when a row is clicked
6174          * @param {Roo.bootstrap.Table} this
6175          * @param {Roo.Element} el
6176          * @param {Number} rowIndex
6177          * @param {Roo.EventObject} e
6178          */
6179         "rowclick" : true,
6180         /**
6181          * @event rowdblclick
6182          * Fires when a row is double clicked
6183          * @param {Roo.bootstrap.Table} this
6184          * @param {Roo.Element} el
6185          * @param {Number} rowIndex
6186          * @param {Roo.EventObject} e
6187          */
6188         "rowdblclick" : true,
6189         /**
6190          * @event mouseover
6191          * Fires when a mouseover occur
6192          * @param {Roo.bootstrap.Table} this
6193          * @param {Roo.Element} el
6194          * @param {Number} rowIndex
6195          * @param {Number} columnIndex
6196          * @param {Roo.EventObject} e
6197          */
6198         "mouseover" : true,
6199         /**
6200          * @event mouseout
6201          * Fires when a mouseout occur
6202          * @param {Roo.bootstrap.Table} this
6203          * @param {Roo.Element} el
6204          * @param {Number} rowIndex
6205          * @param {Number} columnIndex
6206          * @param {Roo.EventObject} e
6207          */
6208         "mouseout" : true,
6209         /**
6210          * @event rowclass
6211          * Fires when a row is rendered, so you can change add a style to it.
6212          * @param {Roo.bootstrap.Table} this
6213          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6214          */
6215         'rowclass' : true,
6216           /**
6217          * @event rowsrendered
6218          * Fires when all the  rows have been rendered
6219          * @param {Roo.bootstrap.Table} this
6220          */
6221         'rowsrendered' : true,
6222         /**
6223          * @event contextmenu
6224          * The raw contextmenu event for the entire grid.
6225          * @param {Roo.EventObject} e
6226          */
6227         "contextmenu" : true,
6228         /**
6229          * @event rowcontextmenu
6230          * Fires when a row is right clicked
6231          * @param {Roo.bootstrap.Table} this
6232          * @param {Number} rowIndex
6233          * @param {Roo.EventObject} e
6234          */
6235         "rowcontextmenu" : true,
6236         /**
6237          * @event cellcontextmenu
6238          * Fires when a cell is right clicked
6239          * @param {Roo.bootstrap.Table} this
6240          * @param {Number} rowIndex
6241          * @param {Number} cellIndex
6242          * @param {Roo.EventObject} e
6243          */
6244          "cellcontextmenu" : true,
6245          /**
6246          * @event headercontextmenu
6247          * Fires when a header is right clicked
6248          * @param {Roo.bootstrap.Table} this
6249          * @param {Number} columnIndex
6250          * @param {Roo.EventObject} e
6251          */
6252         "headercontextmenu" : true
6253     });
6254 };
6255
6256 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6257     
6258     cls: false,
6259     align: false,
6260     bgcolor: false,
6261     border: false,
6262     cellpadding: false,
6263     cellspacing: false,
6264     frame: false,
6265     rules: false,
6266     sortable: false,
6267     summary: false,
6268     width: false,
6269     striped : false,
6270     scrollBody : false,
6271     bordered: false,
6272     hover:  false,
6273     condensed : false,
6274     responsive : false,
6275     sm : false,
6276     cm : false,
6277     store : false,
6278     loadMask : false,
6279     footerShow : true,
6280     headerShow : true,
6281   
6282     rowSelection : false,
6283     cellSelection : false,
6284     layout : false,
6285     
6286     // Roo.Element - the tbody
6287     mainBody: false,
6288     // Roo.Element - thead element
6289     mainHead: false,
6290     
6291     container: false, // used by gridpanel...
6292     
6293     lazyLoad : false,
6294     
6295     CSS : Roo.util.CSS,
6296     
6297     auto_hide_footer : false,
6298     
6299     getAutoCreate : function()
6300     {
6301         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6302         
6303         cfg = {
6304             tag: 'table',
6305             cls : 'table',
6306             cn : []
6307         };
6308         if (this.scrollBody) {
6309             cfg.cls += ' table-body-fixed';
6310         }    
6311         if (this.striped) {
6312             cfg.cls += ' table-striped';
6313         }
6314         
6315         if (this.hover) {
6316             cfg.cls += ' table-hover';
6317         }
6318         if (this.bordered) {
6319             cfg.cls += ' table-bordered';
6320         }
6321         if (this.condensed) {
6322             cfg.cls += ' table-condensed';
6323         }
6324         if (this.responsive) {
6325             cfg.cls += ' table-responsive';
6326         }
6327         
6328         if (this.cls) {
6329             cfg.cls+=  ' ' +this.cls;
6330         }
6331         
6332         // this lot should be simplifed...
6333         var _t = this;
6334         var cp = [
6335             'align',
6336             'bgcolor',
6337             'border',
6338             'cellpadding',
6339             'cellspacing',
6340             'frame',
6341             'rules',
6342             'sortable',
6343             'summary',
6344             'width'
6345         ].forEach(function(k) {
6346             if (_t[k]) {
6347                 cfg[k] = _t[k];
6348             }
6349         });
6350         
6351         
6352         if (this.layout) {
6353             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6354         }
6355         
6356         if(this.store || this.cm){
6357             if(this.headerShow){
6358                 cfg.cn.push(this.renderHeader());
6359             }
6360             
6361             cfg.cn.push(this.renderBody());
6362             
6363             if(this.footerShow){
6364                 cfg.cn.push(this.renderFooter());
6365             }
6366             // where does this come from?
6367             //cfg.cls+=  ' TableGrid';
6368         }
6369         
6370         return { cn : [ cfg ] };
6371     },
6372     
6373     initEvents : function()
6374     {   
6375         if(!this.store || !this.cm){
6376             return;
6377         }
6378         if (this.selModel) {
6379             this.selModel.initEvents();
6380         }
6381         
6382         
6383         //Roo.log('initEvents with ds!!!!');
6384         
6385         this.mainBody = this.el.select('tbody', true).first();
6386         this.mainHead = this.el.select('thead', true).first();
6387         this.mainFoot = this.el.select('tfoot', true).first();
6388         
6389         
6390         
6391         var _this = this;
6392         
6393         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6394             e.on('click', _this.sort, _this);
6395         });
6396         
6397         this.mainBody.on("click", this.onClick, this);
6398         this.mainBody.on("dblclick", this.onDblClick, this);
6399         
6400         // why is this done????? = it breaks dialogs??
6401         //this.parent().el.setStyle('position', 'relative');
6402         
6403         
6404         if (this.footer) {
6405             this.footer.parentId = this.id;
6406             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6407             
6408             if(this.lazyLoad){
6409                 this.el.select('tfoot tr td').first().addClass('hide');
6410             }
6411         } 
6412         
6413         if(this.loadMask) {
6414             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6415         }
6416         
6417         this.store.on('load', this.onLoad, this);
6418         this.store.on('beforeload', this.onBeforeLoad, this);
6419         this.store.on('update', this.onUpdate, this);
6420         this.store.on('add', this.onAdd, this);
6421         this.store.on("clear", this.clear, this);
6422         
6423         this.el.on("contextmenu", this.onContextMenu, this);
6424         
6425         this.mainBody.on('scroll', this.onBodyScroll, this);
6426         
6427         this.cm.on("headerchange", this.onHeaderChange, this);
6428         
6429         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6430         
6431     },
6432     
6433     onContextMenu : function(e, t)
6434     {
6435         this.processEvent("contextmenu", e);
6436     },
6437     
6438     processEvent : function(name, e)
6439     {
6440         if (name != 'touchstart' ) {
6441             this.fireEvent(name, e);    
6442         }
6443         
6444         var t = e.getTarget();
6445         
6446         var cell = Roo.get(t);
6447         
6448         if(!cell){
6449             return;
6450         }
6451         
6452         if(cell.findParent('tfoot', false, true)){
6453             return;
6454         }
6455         
6456         if(cell.findParent('thead', false, true)){
6457             
6458             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6459                 cell = Roo.get(t).findParent('th', false, true);
6460                 if (!cell) {
6461                     Roo.log("failed to find th in thead?");
6462                     Roo.log(e.getTarget());
6463                     return;
6464                 }
6465             }
6466             
6467             var cellIndex = cell.dom.cellIndex;
6468             
6469             var ename = name == 'touchstart' ? 'click' : name;
6470             this.fireEvent("header" + ename, this, cellIndex, e);
6471             
6472             return;
6473         }
6474         
6475         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6476             cell = Roo.get(t).findParent('td', false, true);
6477             if (!cell) {
6478                 Roo.log("failed to find th in tbody?");
6479                 Roo.log(e.getTarget());
6480                 return;
6481             }
6482         }
6483         
6484         var row = cell.findParent('tr', false, true);
6485         var cellIndex = cell.dom.cellIndex;
6486         var rowIndex = row.dom.rowIndex - 1;
6487         
6488         if(row !== false){
6489             
6490             this.fireEvent("row" + name, this, rowIndex, e);
6491             
6492             if(cell !== false){
6493             
6494                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6495             }
6496         }
6497         
6498     },
6499     
6500     onMouseover : function(e, el)
6501     {
6502         var cell = Roo.get(el);
6503         
6504         if(!cell){
6505             return;
6506         }
6507         
6508         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6509             cell = cell.findParent('td', false, true);
6510         }
6511         
6512         var row = cell.findParent('tr', false, true);
6513         var cellIndex = cell.dom.cellIndex;
6514         var rowIndex = row.dom.rowIndex - 1; // start from 0
6515         
6516         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6517         
6518     },
6519     
6520     onMouseout : function(e, el)
6521     {
6522         var cell = Roo.get(el);
6523         
6524         if(!cell){
6525             return;
6526         }
6527         
6528         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6529             cell = cell.findParent('td', false, true);
6530         }
6531         
6532         var row = cell.findParent('tr', false, true);
6533         var cellIndex = cell.dom.cellIndex;
6534         var rowIndex = row.dom.rowIndex - 1; // start from 0
6535         
6536         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6537         
6538     },
6539     
6540     onClick : function(e, el)
6541     {
6542         var cell = Roo.get(el);
6543         
6544         if(!cell || (!this.cellSelection && !this.rowSelection)){
6545             return;
6546         }
6547         
6548         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6549             cell = cell.findParent('td', false, true);
6550         }
6551         
6552         if(!cell || typeof(cell) == 'undefined'){
6553             return;
6554         }
6555         
6556         var row = cell.findParent('tr', false, true);
6557         
6558         if(!row || typeof(row) == 'undefined'){
6559             return;
6560         }
6561         
6562         var cellIndex = cell.dom.cellIndex;
6563         var rowIndex = this.getRowIndex(row);
6564         
6565         // why??? - should these not be based on SelectionModel?
6566         if(this.cellSelection){
6567             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6568         }
6569         
6570         if(this.rowSelection){
6571             this.fireEvent('rowclick', this, row, rowIndex, e);
6572         }
6573         
6574         
6575     },
6576         
6577     onDblClick : function(e,el)
6578     {
6579         var cell = Roo.get(el);
6580         
6581         if(!cell || (!this.cellSelection && !this.rowSelection)){
6582             return;
6583         }
6584         
6585         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6586             cell = cell.findParent('td', false, true);
6587         }
6588         
6589         if(!cell || typeof(cell) == 'undefined'){
6590             return;
6591         }
6592         
6593         var row = cell.findParent('tr', false, true);
6594         
6595         if(!row || typeof(row) == 'undefined'){
6596             return;
6597         }
6598         
6599         var cellIndex = cell.dom.cellIndex;
6600         var rowIndex = this.getRowIndex(row);
6601         
6602         if(this.cellSelection){
6603             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6604         }
6605         
6606         if(this.rowSelection){
6607             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6608         }
6609     },
6610     
6611     sort : function(e,el)
6612     {
6613         var col = Roo.get(el);
6614         
6615         if(!col.hasClass('sortable')){
6616             return;
6617         }
6618         
6619         var sort = col.attr('sort');
6620         var dir = 'ASC';
6621         
6622         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6623             dir = 'DESC';
6624         }
6625         
6626         this.store.sortInfo = {field : sort, direction : dir};
6627         
6628         if (this.footer) {
6629             Roo.log("calling footer first");
6630             this.footer.onClick('first');
6631         } else {
6632         
6633             this.store.load({ params : { start : 0 } });
6634         }
6635     },
6636     
6637     renderHeader : function()
6638     {
6639         var header = {
6640             tag: 'thead',
6641             cn : []
6642         };
6643         
6644         var cm = this.cm;
6645         this.totalWidth = 0;
6646         
6647         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6648             
6649             var config = cm.config[i];
6650             
6651             var c = {
6652                 tag: 'th',
6653                 cls : 'x-hcol-' + i,
6654                 style : '',
6655                 html: cm.getColumnHeader(i)
6656             };
6657             
6658             var hh = '';
6659             
6660             if(typeof(config.sortable) != 'undefined' && config.sortable){
6661                 c.cls = 'sortable';
6662                 c.html = '<i class="glyphicon"></i>' + c.html;
6663             }
6664             
6665             if(typeof(config.lgHeader) != 'undefined'){
6666                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6667             }
6668             
6669             if(typeof(config.mdHeader) != 'undefined'){
6670                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6671             }
6672             
6673             if(typeof(config.smHeader) != 'undefined'){
6674                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6675             }
6676             
6677             if(typeof(config.xsHeader) != 'undefined'){
6678                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6679             }
6680             
6681             if(hh.length){
6682                 c.html = hh;
6683             }
6684             
6685             if(typeof(config.tooltip) != 'undefined'){
6686                 c.tooltip = config.tooltip;
6687             }
6688             
6689             if(typeof(config.colspan) != 'undefined'){
6690                 c.colspan = config.colspan;
6691             }
6692             
6693             if(typeof(config.hidden) != 'undefined' && config.hidden){
6694                 c.style += ' display:none;';
6695             }
6696             
6697             if(typeof(config.dataIndex) != 'undefined'){
6698                 c.sort = config.dataIndex;
6699             }
6700             
6701            
6702             
6703             if(typeof(config.align) != 'undefined' && config.align.length){
6704                 c.style += ' text-align:' + config.align + ';';
6705             }
6706             
6707             if(typeof(config.width) != 'undefined'){
6708                 c.style += ' width:' + config.width + 'px;';
6709                 this.totalWidth += config.width;
6710             } else {
6711                 this.totalWidth += 100; // assume minimum of 100 per column?
6712             }
6713             
6714             if(typeof(config.cls) != 'undefined'){
6715                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6716             }
6717             
6718             ['xs','sm','md','lg'].map(function(size){
6719                 
6720                 if(typeof(config[size]) == 'undefined'){
6721                     return;
6722                 }
6723                 
6724                 if (!config[size]) { // 0 = hidden
6725                     c.cls += ' hidden-' + size;
6726                     return;
6727                 }
6728                 
6729                 c.cls += ' col-' + size + '-' + config[size];
6730
6731             });
6732             
6733             header.cn.push(c)
6734         }
6735         
6736         return header;
6737     },
6738     
6739     renderBody : function()
6740     {
6741         var body = {
6742             tag: 'tbody',
6743             cn : [
6744                 {
6745                     tag: 'tr',
6746                     cn : [
6747                         {
6748                             tag : 'td',
6749                             colspan :  this.cm.getColumnCount()
6750                         }
6751                     ]
6752                 }
6753             ]
6754         };
6755         
6756         return body;
6757     },
6758     
6759     renderFooter : function()
6760     {
6761         var footer = {
6762             tag: 'tfoot',
6763             cn : [
6764                 {
6765                     tag: 'tr',
6766                     cn : [
6767                         {
6768                             tag : 'td',
6769                             colspan :  this.cm.getColumnCount()
6770                         }
6771                     ]
6772                 }
6773             ]
6774         };
6775         
6776         return footer;
6777     },
6778     
6779     
6780     
6781     onLoad : function()
6782     {
6783 //        Roo.log('ds onload');
6784         this.clear();
6785         
6786         var _this = this;
6787         var cm = this.cm;
6788         var ds = this.store;
6789         
6790         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6791             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6792             if (_this.store.sortInfo) {
6793                     
6794                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6795                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6796                 }
6797                 
6798                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6799                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6800                 }
6801             }
6802         });
6803         
6804         var tbody =  this.mainBody;
6805               
6806         if(ds.getCount() > 0){
6807             ds.data.each(function(d,rowIndex){
6808                 var row =  this.renderRow(cm, ds, rowIndex);
6809                 
6810                 tbody.createChild(row);
6811                 
6812                 var _this = this;
6813                 
6814                 if(row.cellObjects.length){
6815                     Roo.each(row.cellObjects, function(r){
6816                         _this.renderCellObject(r);
6817                     })
6818                 }
6819                 
6820             }, this);
6821         }
6822         
6823         var tfoot = this.el.select('tfoot', true).first();
6824         
6825         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6826             
6827             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6828             
6829             var total = this.ds.getTotalCount();
6830             
6831             if(this.footer.pageSize < total){
6832                 this.mainFoot.show();
6833             }
6834         }
6835         
6836         Roo.each(this.el.select('tbody td', true).elements, function(e){
6837             e.on('mouseover', _this.onMouseover, _this);
6838         });
6839         
6840         Roo.each(this.el.select('tbody td', true).elements, function(e){
6841             e.on('mouseout', _this.onMouseout, _this);
6842         });
6843         this.fireEvent('rowsrendered', this);
6844         
6845         this.autoSize();
6846     },
6847     
6848     
6849     onUpdate : function(ds,record)
6850     {
6851         this.refreshRow(record);
6852         this.autoSize();
6853     },
6854     
6855     onRemove : function(ds, record, index, isUpdate){
6856         if(isUpdate !== true){
6857             this.fireEvent("beforerowremoved", this, index, record);
6858         }
6859         var bt = this.mainBody.dom;
6860         
6861         var rows = this.el.select('tbody > tr', true).elements;
6862         
6863         if(typeof(rows[index]) != 'undefined'){
6864             bt.removeChild(rows[index].dom);
6865         }
6866         
6867 //        if(bt.rows[index]){
6868 //            bt.removeChild(bt.rows[index]);
6869 //        }
6870         
6871         if(isUpdate !== true){
6872             //this.stripeRows(index);
6873             //this.syncRowHeights(index, index);
6874             //this.layout();
6875             this.fireEvent("rowremoved", this, index, record);
6876         }
6877     },
6878     
6879     onAdd : function(ds, records, rowIndex)
6880     {
6881         //Roo.log('on Add called');
6882         // - note this does not handle multiple adding very well..
6883         var bt = this.mainBody.dom;
6884         for (var i =0 ; i < records.length;i++) {
6885             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6886             //Roo.log(records[i]);
6887             //Roo.log(this.store.getAt(rowIndex+i));
6888             this.insertRow(this.store, rowIndex + i, false);
6889             return;
6890         }
6891         
6892     },
6893     
6894     
6895     refreshRow : function(record){
6896         var ds = this.store, index;
6897         if(typeof record == 'number'){
6898             index = record;
6899             record = ds.getAt(index);
6900         }else{
6901             index = ds.indexOf(record);
6902         }
6903         this.insertRow(ds, index, true);
6904         this.autoSize();
6905         this.onRemove(ds, record, index+1, true);
6906         this.autoSize();
6907         //this.syncRowHeights(index, index);
6908         //this.layout();
6909         this.fireEvent("rowupdated", this, index, record);
6910     },
6911     
6912     insertRow : function(dm, rowIndex, isUpdate){
6913         
6914         if(!isUpdate){
6915             this.fireEvent("beforerowsinserted", this, rowIndex);
6916         }
6917             //var s = this.getScrollState();
6918         var row = this.renderRow(this.cm, this.store, rowIndex);
6919         // insert before rowIndex..
6920         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6921         
6922         var _this = this;
6923                 
6924         if(row.cellObjects.length){
6925             Roo.each(row.cellObjects, function(r){
6926                 _this.renderCellObject(r);
6927             })
6928         }
6929             
6930         if(!isUpdate){
6931             this.fireEvent("rowsinserted", this, rowIndex);
6932             //this.syncRowHeights(firstRow, lastRow);
6933             //this.stripeRows(firstRow);
6934             //this.layout();
6935         }
6936         
6937     },
6938     
6939     
6940     getRowDom : function(rowIndex)
6941     {
6942         var rows = this.el.select('tbody > tr', true).elements;
6943         
6944         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6945         
6946     },
6947     // returns the object tree for a tr..
6948   
6949     
6950     renderRow : function(cm, ds, rowIndex) 
6951     {
6952         var d = ds.getAt(rowIndex);
6953         
6954         var row = {
6955             tag : 'tr',
6956             cls : 'x-row-' + rowIndex,
6957             cn : []
6958         };
6959             
6960         var cellObjects = [];
6961         
6962         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6963             var config = cm.config[i];
6964             
6965             var renderer = cm.getRenderer(i);
6966             var value = '';
6967             var id = false;
6968             
6969             if(typeof(renderer) !== 'undefined'){
6970                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6971             }
6972             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6973             // and are rendered into the cells after the row is rendered - using the id for the element.
6974             
6975             if(typeof(value) === 'object'){
6976                 id = Roo.id();
6977                 cellObjects.push({
6978                     container : id,
6979                     cfg : value 
6980                 })
6981             }
6982             
6983             var rowcfg = {
6984                 record: d,
6985                 rowIndex : rowIndex,
6986                 colIndex : i,
6987                 rowClass : ''
6988             };
6989
6990             this.fireEvent('rowclass', this, rowcfg);
6991             
6992             var td = {
6993                 tag: 'td',
6994                 cls : rowcfg.rowClass + ' x-col-' + i,
6995                 style: '',
6996                 html: (typeof(value) === 'object') ? '' : value
6997             };
6998             
6999             if (id) {
7000                 td.id = id;
7001             }
7002             
7003             if(typeof(config.colspan) != 'undefined'){
7004                 td.colspan = config.colspan;
7005             }
7006             
7007             if(typeof(config.hidden) != 'undefined' && config.hidden){
7008                 td.style += ' display:none;';
7009             }
7010             
7011             if(typeof(config.align) != 'undefined' && config.align.length){
7012                 td.style += ' text-align:' + config.align + ';';
7013             }
7014             if(typeof(config.valign) != 'undefined' && config.valign.length){
7015                 td.style += ' vertical-align:' + config.valign + ';';
7016             }
7017             
7018             if(typeof(config.width) != 'undefined'){
7019                 td.style += ' width:' +  config.width + 'px;';
7020             }
7021             
7022             if(typeof(config.cursor) != 'undefined'){
7023                 td.style += ' cursor:' +  config.cursor + ';';
7024             }
7025             
7026             if(typeof(config.cls) != 'undefined'){
7027                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7028             }
7029             
7030             ['xs','sm','md','lg'].map(function(size){
7031                 
7032                 if(typeof(config[size]) == 'undefined'){
7033                     return;
7034                 }
7035                 
7036                 if (!config[size]) { // 0 = hidden
7037                     td.cls += ' hidden-' + size;
7038                     return;
7039                 }
7040                 
7041                 td.cls += ' col-' + size + '-' + config[size];
7042
7043             });
7044             
7045             row.cn.push(td);
7046            
7047         }
7048         
7049         row.cellObjects = cellObjects;
7050         
7051         return row;
7052           
7053     },
7054     
7055     
7056     
7057     onBeforeLoad : function()
7058     {
7059         
7060     },
7061      /**
7062      * Remove all rows
7063      */
7064     clear : function()
7065     {
7066         this.el.select('tbody', true).first().dom.innerHTML = '';
7067     },
7068     /**
7069      * Show or hide a row.
7070      * @param {Number} rowIndex to show or hide
7071      * @param {Boolean} state hide
7072      */
7073     setRowVisibility : function(rowIndex, state)
7074     {
7075         var bt = this.mainBody.dom;
7076         
7077         var rows = this.el.select('tbody > tr', true).elements;
7078         
7079         if(typeof(rows[rowIndex]) == 'undefined'){
7080             return;
7081         }
7082         rows[rowIndex].dom.style.display = state ? '' : 'none';
7083     },
7084     
7085     
7086     getSelectionModel : function(){
7087         if(!this.selModel){
7088             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7089         }
7090         return this.selModel;
7091     },
7092     /*
7093      * Render the Roo.bootstrap object from renderder
7094      */
7095     renderCellObject : function(r)
7096     {
7097         var _this = this;
7098         
7099         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7100         
7101         var t = r.cfg.render(r.container);
7102         
7103         if(r.cfg.cn){
7104             Roo.each(r.cfg.cn, function(c){
7105                 var child = {
7106                     container: t.getChildContainer(),
7107                     cfg: c
7108                 };
7109                 _this.renderCellObject(child);
7110             })
7111         }
7112     },
7113     
7114     getRowIndex : function(row)
7115     {
7116         var rowIndex = -1;
7117         
7118         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7119             if(el != row){
7120                 return;
7121             }
7122             
7123             rowIndex = index;
7124         });
7125         
7126         return rowIndex;
7127     },
7128      /**
7129      * Returns the grid's underlying element = used by panel.Grid
7130      * @return {Element} The element
7131      */
7132     getGridEl : function(){
7133         return this.el;
7134     },
7135      /**
7136      * Forces a resize - used by panel.Grid
7137      * @return {Element} The element
7138      */
7139     autoSize : function()
7140     {
7141         //var ctr = Roo.get(this.container.dom.parentElement);
7142         var ctr = Roo.get(this.el.dom);
7143         
7144         var thd = this.getGridEl().select('thead',true).first();
7145         var tbd = this.getGridEl().select('tbody', true).first();
7146         var tfd = this.getGridEl().select('tfoot', true).first();
7147         
7148         var cw = ctr.getWidth();
7149         
7150         if (tbd) {
7151             
7152             tbd.setSize(ctr.getWidth(),
7153                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7154             );
7155             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7156             cw -= barsize;
7157         }
7158         cw = Math.max(cw, this.totalWidth);
7159         this.getGridEl().select('tr',true).setWidth(cw);
7160         // resize 'expandable coloumn?
7161         
7162         return; // we doe not have a view in this design..
7163         
7164     },
7165     onBodyScroll: function()
7166     {
7167         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7168         if(this.mainHead){
7169             this.mainHead.setStyle({
7170                 'position' : 'relative',
7171                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7172             });
7173         }
7174         
7175         if(this.lazyLoad){
7176             
7177             var scrollHeight = this.mainBody.dom.scrollHeight;
7178             
7179             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7180             
7181             var height = this.mainBody.getHeight();
7182             
7183             if(scrollHeight - height == scrollTop) {
7184                 
7185                 var total = this.ds.getTotalCount();
7186                 
7187                 if(this.footer.cursor + this.footer.pageSize < total){
7188                     
7189                     this.footer.ds.load({
7190                         params : {
7191                             start : this.footer.cursor + this.footer.pageSize,
7192                             limit : this.footer.pageSize
7193                         },
7194                         add : true
7195                     });
7196                 }
7197             }
7198             
7199         }
7200     },
7201     
7202     onHeaderChange : function()
7203     {
7204         var header = this.renderHeader();
7205         var table = this.el.select('table', true).first();
7206         
7207         this.mainHead.remove();
7208         this.mainHead = table.createChild(header, this.mainBody, false);
7209     },
7210     
7211     onHiddenChange : function(colModel, colIndex, hidden)
7212     {
7213         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7214         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7215         
7216         this.CSS.updateRule(thSelector, "display", "");
7217         this.CSS.updateRule(tdSelector, "display", "");
7218         
7219         if(hidden){
7220             this.CSS.updateRule(thSelector, "display", "none");
7221             this.CSS.updateRule(tdSelector, "display", "none");
7222         }
7223         
7224         this.onHeaderChange();
7225         this.onLoad();
7226     },
7227     
7228     setColumnWidth: function(col_index, width)
7229     {
7230         // width = "md-2 xs-2..."
7231         if(!this.colModel.config[col_index]) {
7232             return;
7233         }
7234         
7235         var w = width.split(" ");
7236         
7237         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7238         
7239         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7240         
7241         
7242         for(var j = 0; j < w.length; j++) {
7243             
7244             if(!w[j]) {
7245                 continue;
7246             }
7247             
7248             var size_cls = w[j].split("-");
7249             
7250             if(!Number.isInteger(size_cls[1] * 1)) {
7251                 continue;
7252             }
7253             
7254             if(!this.colModel.config[col_index][size_cls[0]]) {
7255                 continue;
7256             }
7257             
7258             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7259                 continue;
7260             }
7261             
7262             h_row[0].classList.replace(
7263                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7264                 "col-"+size_cls[0]+"-"+size_cls[1]
7265             );
7266             
7267             for(var i = 0; i < rows.length; i++) {
7268                 
7269                 var size_cls = w[j].split("-");
7270                 
7271                 if(!Number.isInteger(size_cls[1] * 1)) {
7272                     continue;
7273                 }
7274                 
7275                 if(!this.colModel.config[col_index][size_cls[0]]) {
7276                     continue;
7277                 }
7278                 
7279                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7280                     continue;
7281                 }
7282                 
7283                 rows[i].classList.replace(
7284                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7285                     "col-"+size_cls[0]+"-"+size_cls[1]
7286                 );
7287             }
7288             
7289             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7290         }
7291     }
7292 });
7293
7294  
7295
7296  /*
7297  * - LGPL
7298  *
7299  * table cell
7300  * 
7301  */
7302
7303 /**
7304  * @class Roo.bootstrap.TableCell
7305  * @extends Roo.bootstrap.Component
7306  * Bootstrap TableCell class
7307  * @cfg {String} html cell contain text
7308  * @cfg {String} cls cell class
7309  * @cfg {String} tag cell tag (td|th) default td
7310  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7311  * @cfg {String} align Aligns the content in a cell
7312  * @cfg {String} axis Categorizes cells
7313  * @cfg {String} bgcolor Specifies the background color of a cell
7314  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7315  * @cfg {Number} colspan Specifies the number of columns a cell should span
7316  * @cfg {String} headers Specifies one or more header cells a cell is related to
7317  * @cfg {Number} height Sets the height of a cell
7318  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7319  * @cfg {Number} rowspan Sets the number of rows a cell should span
7320  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7321  * @cfg {String} valign Vertical aligns the content in a cell
7322  * @cfg {Number} width Specifies the width of a cell
7323  * 
7324  * @constructor
7325  * Create a new TableCell
7326  * @param {Object} config The config object
7327  */
7328
7329 Roo.bootstrap.TableCell = function(config){
7330     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7331 };
7332
7333 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7334     
7335     html: false,
7336     cls: false,
7337     tag: false,
7338     abbr: false,
7339     align: false,
7340     axis: false,
7341     bgcolor: false,
7342     charoff: false,
7343     colspan: false,
7344     headers: false,
7345     height: false,
7346     nowrap: false,
7347     rowspan: false,
7348     scope: false,
7349     valign: false,
7350     width: false,
7351     
7352     
7353     getAutoCreate : function(){
7354         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7355         
7356         cfg = {
7357             tag: 'td'
7358         };
7359         
7360         if(this.tag){
7361             cfg.tag = this.tag;
7362         }
7363         
7364         if (this.html) {
7365             cfg.html=this.html
7366         }
7367         if (this.cls) {
7368             cfg.cls=this.cls
7369         }
7370         if (this.abbr) {
7371             cfg.abbr=this.abbr
7372         }
7373         if (this.align) {
7374             cfg.align=this.align
7375         }
7376         if (this.axis) {
7377             cfg.axis=this.axis
7378         }
7379         if (this.bgcolor) {
7380             cfg.bgcolor=this.bgcolor
7381         }
7382         if (this.charoff) {
7383             cfg.charoff=this.charoff
7384         }
7385         if (this.colspan) {
7386             cfg.colspan=this.colspan
7387         }
7388         if (this.headers) {
7389             cfg.headers=this.headers
7390         }
7391         if (this.height) {
7392             cfg.height=this.height
7393         }
7394         if (this.nowrap) {
7395             cfg.nowrap=this.nowrap
7396         }
7397         if (this.rowspan) {
7398             cfg.rowspan=this.rowspan
7399         }
7400         if (this.scope) {
7401             cfg.scope=this.scope
7402         }
7403         if (this.valign) {
7404             cfg.valign=this.valign
7405         }
7406         if (this.width) {
7407             cfg.width=this.width
7408         }
7409         
7410         
7411         return cfg;
7412     }
7413    
7414 });
7415
7416  
7417
7418  /*
7419  * - LGPL
7420  *
7421  * table row
7422  * 
7423  */
7424
7425 /**
7426  * @class Roo.bootstrap.TableRow
7427  * @extends Roo.bootstrap.Component
7428  * Bootstrap TableRow class
7429  * @cfg {String} cls row class
7430  * @cfg {String} align Aligns the content in a table row
7431  * @cfg {String} bgcolor Specifies a background color for a table row
7432  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7433  * @cfg {String} valign Vertical aligns the content in a table row
7434  * 
7435  * @constructor
7436  * Create a new TableRow
7437  * @param {Object} config The config object
7438  */
7439
7440 Roo.bootstrap.TableRow = function(config){
7441     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7442 };
7443
7444 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7445     
7446     cls: false,
7447     align: false,
7448     bgcolor: false,
7449     charoff: false,
7450     valign: false,
7451     
7452     getAutoCreate : function(){
7453         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7454         
7455         cfg = {
7456             tag: 'tr'
7457         };
7458             
7459         if(this.cls){
7460             cfg.cls = this.cls;
7461         }
7462         if(this.align){
7463             cfg.align = this.align;
7464         }
7465         if(this.bgcolor){
7466             cfg.bgcolor = this.bgcolor;
7467         }
7468         if(this.charoff){
7469             cfg.charoff = this.charoff;
7470         }
7471         if(this.valign){
7472             cfg.valign = this.valign;
7473         }
7474         
7475         return cfg;
7476     }
7477    
7478 });
7479
7480  
7481
7482  /*
7483  * - LGPL
7484  *
7485  * table body
7486  * 
7487  */
7488
7489 /**
7490  * @class Roo.bootstrap.TableBody
7491  * @extends Roo.bootstrap.Component
7492  * Bootstrap TableBody class
7493  * @cfg {String} cls element class
7494  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7495  * @cfg {String} align Aligns the content inside the element
7496  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7497  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7498  * 
7499  * @constructor
7500  * Create a new TableBody
7501  * @param {Object} config The config object
7502  */
7503
7504 Roo.bootstrap.TableBody = function(config){
7505     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7506 };
7507
7508 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7509     
7510     cls: false,
7511     tag: false,
7512     align: false,
7513     charoff: false,
7514     valign: false,
7515     
7516     getAutoCreate : function(){
7517         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7518         
7519         cfg = {
7520             tag: 'tbody'
7521         };
7522             
7523         if (this.cls) {
7524             cfg.cls=this.cls
7525         }
7526         if(this.tag){
7527             cfg.tag = this.tag;
7528         }
7529         
7530         if(this.align){
7531             cfg.align = this.align;
7532         }
7533         if(this.charoff){
7534             cfg.charoff = this.charoff;
7535         }
7536         if(this.valign){
7537             cfg.valign = this.valign;
7538         }
7539         
7540         return cfg;
7541     }
7542     
7543     
7544 //    initEvents : function()
7545 //    {
7546 //        
7547 //        if(!this.store){
7548 //            return;
7549 //        }
7550 //        
7551 //        this.store = Roo.factory(this.store, Roo.data);
7552 //        this.store.on('load', this.onLoad, this);
7553 //        
7554 //        this.store.load();
7555 //        
7556 //    },
7557 //    
7558 //    onLoad: function () 
7559 //    {   
7560 //        this.fireEvent('load', this);
7561 //    }
7562 //    
7563 //   
7564 });
7565
7566  
7567
7568  /*
7569  * Based on:
7570  * Ext JS Library 1.1.1
7571  * Copyright(c) 2006-2007, Ext JS, LLC.
7572  *
7573  * Originally Released Under LGPL - original licence link has changed is not relivant.
7574  *
7575  * Fork - LGPL
7576  * <script type="text/javascript">
7577  */
7578
7579 // as we use this in bootstrap.
7580 Roo.namespace('Roo.form');
7581  /**
7582  * @class Roo.form.Action
7583  * Internal Class used to handle form actions
7584  * @constructor
7585  * @param {Roo.form.BasicForm} el The form element or its id
7586  * @param {Object} config Configuration options
7587  */
7588
7589  
7590  
7591 // define the action interface
7592 Roo.form.Action = function(form, options){
7593     this.form = form;
7594     this.options = options || {};
7595 };
7596 /**
7597  * Client Validation Failed
7598  * @const 
7599  */
7600 Roo.form.Action.CLIENT_INVALID = 'client';
7601 /**
7602  * Server Validation Failed
7603  * @const 
7604  */
7605 Roo.form.Action.SERVER_INVALID = 'server';
7606  /**
7607  * Connect to Server Failed
7608  * @const 
7609  */
7610 Roo.form.Action.CONNECT_FAILURE = 'connect';
7611 /**
7612  * Reading Data from Server Failed
7613  * @const 
7614  */
7615 Roo.form.Action.LOAD_FAILURE = 'load';
7616
7617 Roo.form.Action.prototype = {
7618     type : 'default',
7619     failureType : undefined,
7620     response : undefined,
7621     result : undefined,
7622
7623     // interface method
7624     run : function(options){
7625
7626     },
7627
7628     // interface method
7629     success : function(response){
7630
7631     },
7632
7633     // interface method
7634     handleResponse : function(response){
7635
7636     },
7637
7638     // default connection failure
7639     failure : function(response){
7640         
7641         this.response = response;
7642         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7643         this.form.afterAction(this, false);
7644     },
7645
7646     processResponse : function(response){
7647         this.response = response;
7648         if(!response.responseText){
7649             return true;
7650         }
7651         this.result = this.handleResponse(response);
7652         return this.result;
7653     },
7654
7655     // utility functions used internally
7656     getUrl : function(appendParams){
7657         var url = this.options.url || this.form.url || this.form.el.dom.action;
7658         if(appendParams){
7659             var p = this.getParams();
7660             if(p){
7661                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7662             }
7663         }
7664         return url;
7665     },
7666
7667     getMethod : function(){
7668         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7669     },
7670
7671     getParams : function(){
7672         var bp = this.form.baseParams;
7673         var p = this.options.params;
7674         if(p){
7675             if(typeof p == "object"){
7676                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7677             }else if(typeof p == 'string' && bp){
7678                 p += '&' + Roo.urlEncode(bp);
7679             }
7680         }else if(bp){
7681             p = Roo.urlEncode(bp);
7682         }
7683         return p;
7684     },
7685
7686     createCallback : function(){
7687         return {
7688             success: this.success,
7689             failure: this.failure,
7690             scope: this,
7691             timeout: (this.form.timeout*1000),
7692             upload: this.form.fileUpload ? this.success : undefined
7693         };
7694     }
7695 };
7696
7697 Roo.form.Action.Submit = function(form, options){
7698     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7699 };
7700
7701 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7702     type : 'submit',
7703
7704     haveProgress : false,
7705     uploadComplete : false,
7706     
7707     // uploadProgress indicator.
7708     uploadProgress : function()
7709     {
7710         if (!this.form.progressUrl) {
7711             return;
7712         }
7713         
7714         if (!this.haveProgress) {
7715             Roo.MessageBox.progress("Uploading", "Uploading");
7716         }
7717         if (this.uploadComplete) {
7718            Roo.MessageBox.hide();
7719            return;
7720         }
7721         
7722         this.haveProgress = true;
7723    
7724         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7725         
7726         var c = new Roo.data.Connection();
7727         c.request({
7728             url : this.form.progressUrl,
7729             params: {
7730                 id : uid
7731             },
7732             method: 'GET',
7733             success : function(req){
7734                //console.log(data);
7735                 var rdata = false;
7736                 var edata;
7737                 try  {
7738                    rdata = Roo.decode(req.responseText)
7739                 } catch (e) {
7740                     Roo.log("Invalid data from server..");
7741                     Roo.log(edata);
7742                     return;
7743                 }
7744                 if (!rdata || !rdata.success) {
7745                     Roo.log(rdata);
7746                     Roo.MessageBox.alert(Roo.encode(rdata));
7747                     return;
7748                 }
7749                 var data = rdata.data;
7750                 
7751                 if (this.uploadComplete) {
7752                    Roo.MessageBox.hide();
7753                    return;
7754                 }
7755                    
7756                 if (data){
7757                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7758                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7759                     );
7760                 }
7761                 this.uploadProgress.defer(2000,this);
7762             },
7763        
7764             failure: function(data) {
7765                 Roo.log('progress url failed ');
7766                 Roo.log(data);
7767             },
7768             scope : this
7769         });
7770            
7771     },
7772     
7773     
7774     run : function()
7775     {
7776         // run get Values on the form, so it syncs any secondary forms.
7777         this.form.getValues();
7778         
7779         var o = this.options;
7780         var method = this.getMethod();
7781         var isPost = method == 'POST';
7782         if(o.clientValidation === false || this.form.isValid()){
7783             
7784             if (this.form.progressUrl) {
7785                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7786                     (new Date() * 1) + '' + Math.random());
7787                     
7788             } 
7789             
7790             
7791             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7792                 form:this.form.el.dom,
7793                 url:this.getUrl(!isPost),
7794                 method: method,
7795                 params:isPost ? this.getParams() : null,
7796                 isUpload: this.form.fileUpload
7797             }));
7798             
7799             this.uploadProgress();
7800
7801         }else if (o.clientValidation !== false){ // client validation failed
7802             this.failureType = Roo.form.Action.CLIENT_INVALID;
7803             this.form.afterAction(this, false);
7804         }
7805     },
7806
7807     success : function(response)
7808     {
7809         this.uploadComplete= true;
7810         if (this.haveProgress) {
7811             Roo.MessageBox.hide();
7812         }
7813         
7814         
7815         var result = this.processResponse(response);
7816         if(result === true || result.success){
7817             this.form.afterAction(this, true);
7818             return;
7819         }
7820         if(result.errors){
7821             this.form.markInvalid(result.errors);
7822             this.failureType = Roo.form.Action.SERVER_INVALID;
7823         }
7824         this.form.afterAction(this, false);
7825     },
7826     failure : function(response)
7827     {
7828         this.uploadComplete= true;
7829         if (this.haveProgress) {
7830             Roo.MessageBox.hide();
7831         }
7832         
7833         this.response = response;
7834         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7835         this.form.afterAction(this, false);
7836     },
7837     
7838     handleResponse : function(response){
7839         if(this.form.errorReader){
7840             var rs = this.form.errorReader.read(response);
7841             var errors = [];
7842             if(rs.records){
7843                 for(var i = 0, len = rs.records.length; i < len; i++) {
7844                     var r = rs.records[i];
7845                     errors[i] = r.data;
7846                 }
7847             }
7848             if(errors.length < 1){
7849                 errors = null;
7850             }
7851             return {
7852                 success : rs.success,
7853                 errors : errors
7854             };
7855         }
7856         var ret = false;
7857         try {
7858             ret = Roo.decode(response.responseText);
7859         } catch (e) {
7860             ret = {
7861                 success: false,
7862                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7863                 errors : []
7864             };
7865         }
7866         return ret;
7867         
7868     }
7869 });
7870
7871
7872 Roo.form.Action.Load = function(form, options){
7873     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7874     this.reader = this.form.reader;
7875 };
7876
7877 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7878     type : 'load',
7879
7880     run : function(){
7881         
7882         Roo.Ajax.request(Roo.apply(
7883                 this.createCallback(), {
7884                     method:this.getMethod(),
7885                     url:this.getUrl(false),
7886                     params:this.getParams()
7887         }));
7888     },
7889
7890     success : function(response){
7891         
7892         var result = this.processResponse(response);
7893         if(result === true || !result.success || !result.data){
7894             this.failureType = Roo.form.Action.LOAD_FAILURE;
7895             this.form.afterAction(this, false);
7896             return;
7897         }
7898         this.form.clearInvalid();
7899         this.form.setValues(result.data);
7900         this.form.afterAction(this, true);
7901     },
7902
7903     handleResponse : function(response){
7904         if(this.form.reader){
7905             var rs = this.form.reader.read(response);
7906             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7907             return {
7908                 success : rs.success,
7909                 data : data
7910             };
7911         }
7912         return Roo.decode(response.responseText);
7913     }
7914 });
7915
7916 Roo.form.Action.ACTION_TYPES = {
7917     'load' : Roo.form.Action.Load,
7918     'submit' : Roo.form.Action.Submit
7919 };/*
7920  * - LGPL
7921  *
7922  * form
7923  *
7924  */
7925
7926 /**
7927  * @class Roo.bootstrap.Form
7928  * @extends Roo.bootstrap.Component
7929  * Bootstrap Form class
7930  * @cfg {String} method  GET | POST (default POST)
7931  * @cfg {String} labelAlign top | left (default top)
7932  * @cfg {String} align left  | right - for navbars
7933  * @cfg {Boolean} loadMask load mask when submit (default true)
7934
7935  *
7936  * @constructor
7937  * Create a new Form
7938  * @param {Object} config The config object
7939  */
7940
7941
7942 Roo.bootstrap.Form = function(config){
7943     
7944     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7945     
7946     Roo.bootstrap.Form.popover.apply();
7947     
7948     this.addEvents({
7949         /**
7950          * @event clientvalidation
7951          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7952          * @param {Form} this
7953          * @param {Boolean} valid true if the form has passed client-side validation
7954          */
7955         clientvalidation: true,
7956         /**
7957          * @event beforeaction
7958          * Fires before any action is performed. Return false to cancel the action.
7959          * @param {Form} this
7960          * @param {Action} action The action to be performed
7961          */
7962         beforeaction: true,
7963         /**
7964          * @event actionfailed
7965          * Fires when an action fails.
7966          * @param {Form} this
7967          * @param {Action} action The action that failed
7968          */
7969         actionfailed : true,
7970         /**
7971          * @event actioncomplete
7972          * Fires when an action is completed.
7973          * @param {Form} this
7974          * @param {Action} action The action that completed
7975          */
7976         actioncomplete : true
7977     });
7978 };
7979
7980 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7981
7982      /**
7983      * @cfg {String} method
7984      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7985      */
7986     method : 'POST',
7987     /**
7988      * @cfg {String} url
7989      * The URL to use for form actions if one isn't supplied in the action options.
7990      */
7991     /**
7992      * @cfg {Boolean} fileUpload
7993      * Set to true if this form is a file upload.
7994      */
7995
7996     /**
7997      * @cfg {Object} baseParams
7998      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7999      */
8000
8001     /**
8002      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8003      */
8004     timeout: 30,
8005     /**
8006      * @cfg {Sting} align (left|right) for navbar forms
8007      */
8008     align : 'left',
8009
8010     // private
8011     activeAction : null,
8012
8013     /**
8014      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8015      * element by passing it or its id or mask the form itself by passing in true.
8016      * @type Mixed
8017      */
8018     waitMsgTarget : false,
8019
8020     loadMask : true,
8021     
8022     /**
8023      * @cfg {Boolean} errorMask (true|false) default false
8024      */
8025     errorMask : false,
8026     
8027     /**
8028      * @cfg {Number} maskOffset Default 100
8029      */
8030     maskOffset : 100,
8031     
8032     /**
8033      * @cfg {Boolean} maskBody
8034      */
8035     maskBody : false,
8036
8037     getAutoCreate : function(){
8038
8039         var cfg = {
8040             tag: 'form',
8041             method : this.method || 'POST',
8042             id : this.id || Roo.id(),
8043             cls : ''
8044         };
8045         if (this.parent().xtype.match(/^Nav/)) {
8046             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8047
8048         }
8049
8050         if (this.labelAlign == 'left' ) {
8051             cfg.cls += ' form-horizontal';
8052         }
8053
8054
8055         return cfg;
8056     },
8057     initEvents : function()
8058     {
8059         this.el.on('submit', this.onSubmit, this);
8060         // this was added as random key presses on the form where triggering form submit.
8061         this.el.on('keypress', function(e) {
8062             if (e.getCharCode() != 13) {
8063                 return true;
8064             }
8065             // we might need to allow it for textareas.. and some other items.
8066             // check e.getTarget().
8067
8068             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8069                 return true;
8070             }
8071
8072             Roo.log("keypress blocked");
8073
8074             e.preventDefault();
8075             return false;
8076         });
8077         
8078     },
8079     // private
8080     onSubmit : function(e){
8081         e.stopEvent();
8082     },
8083
8084      /**
8085      * Returns true if client-side validation on the form is successful.
8086      * @return Boolean
8087      */
8088     isValid : function(){
8089         var items = this.getItems();
8090         var valid = true;
8091         var target = false;
8092         
8093         items.each(function(f){
8094             
8095             if(f.validate()){
8096                 return;
8097             }
8098             
8099             Roo.log('invalid field: ' + f.name);
8100             
8101             valid = false;
8102
8103             if(!target && f.el.isVisible(true)){
8104                 target = f;
8105             }
8106            
8107         });
8108         
8109         if(this.errorMask && !valid){
8110             Roo.bootstrap.Form.popover.mask(this, target);
8111         }
8112         
8113         return valid;
8114     },
8115     
8116     /**
8117      * Returns true if any fields in this form have changed since their original load.
8118      * @return Boolean
8119      */
8120     isDirty : function(){
8121         var dirty = false;
8122         var items = this.getItems();
8123         items.each(function(f){
8124            if(f.isDirty()){
8125                dirty = true;
8126                return false;
8127            }
8128            return true;
8129         });
8130         return dirty;
8131     },
8132      /**
8133      * Performs a predefined action (submit or load) or custom actions you define on this form.
8134      * @param {String} actionName The name of the action type
8135      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8136      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8137      * accept other config options):
8138      * <pre>
8139 Property          Type             Description
8140 ----------------  ---------------  ----------------------------------------------------------------------------------
8141 url               String           The url for the action (defaults to the form's url)
8142 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8143 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8144 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8145                                    validate the form on the client (defaults to false)
8146      * </pre>
8147      * @return {BasicForm} this
8148      */
8149     doAction : function(action, options){
8150         if(typeof action == 'string'){
8151             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8152         }
8153         if(this.fireEvent('beforeaction', this, action) !== false){
8154             this.beforeAction(action);
8155             action.run.defer(100, action);
8156         }
8157         return this;
8158     },
8159
8160     // private
8161     beforeAction : function(action){
8162         var o = action.options;
8163         
8164         if(this.loadMask){
8165             
8166             if(this.maskBody){
8167                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8168             } else {
8169                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8170             }
8171         }
8172         // not really supported yet.. ??
8173
8174         //if(this.waitMsgTarget === true){
8175         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8176         //}else if(this.waitMsgTarget){
8177         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8178         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8179         //}else {
8180         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8181        // }
8182
8183     },
8184
8185     // private
8186     afterAction : function(action, success){
8187         this.activeAction = null;
8188         var o = action.options;
8189
8190         if(this.loadMask){
8191             
8192             if(this.maskBody){
8193                 Roo.get(document.body).unmask();
8194             } else {
8195                 this.el.unmask();
8196             }
8197         }
8198         
8199         //if(this.waitMsgTarget === true){
8200 //            this.el.unmask();
8201         //}else if(this.waitMsgTarget){
8202         //    this.waitMsgTarget.unmask();
8203         //}else{
8204         //    Roo.MessageBox.updateProgress(1);
8205         //    Roo.MessageBox.hide();
8206        // }
8207         //
8208         if(success){
8209             if(o.reset){
8210                 this.reset();
8211             }
8212             Roo.callback(o.success, o.scope, [this, action]);
8213             this.fireEvent('actioncomplete', this, action);
8214
8215         }else{
8216
8217             // failure condition..
8218             // we have a scenario where updates need confirming.
8219             // eg. if a locking scenario exists..
8220             // we look for { errors : { needs_confirm : true }} in the response.
8221             if (
8222                 (typeof(action.result) != 'undefined')  &&
8223                 (typeof(action.result.errors) != 'undefined')  &&
8224                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8225            ){
8226                 var _t = this;
8227                 Roo.log("not supported yet");
8228                  /*
8229
8230                 Roo.MessageBox.confirm(
8231                     "Change requires confirmation",
8232                     action.result.errorMsg,
8233                     function(r) {
8234                         if (r != 'yes') {
8235                             return;
8236                         }
8237                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8238                     }
8239
8240                 );
8241                 */
8242
8243
8244                 return;
8245             }
8246
8247             Roo.callback(o.failure, o.scope, [this, action]);
8248             // show an error message if no failed handler is set..
8249             if (!this.hasListener('actionfailed')) {
8250                 Roo.log("need to add dialog support");
8251                 /*
8252                 Roo.MessageBox.alert("Error",
8253                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8254                         action.result.errorMsg :
8255                         "Saving Failed, please check your entries or try again"
8256                 );
8257                 */
8258             }
8259
8260             this.fireEvent('actionfailed', this, action);
8261         }
8262
8263     },
8264     /**
8265      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8266      * @param {String} id The value to search for
8267      * @return Field
8268      */
8269     findField : function(id){
8270         var items = this.getItems();
8271         var field = items.get(id);
8272         if(!field){
8273              items.each(function(f){
8274                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8275                     field = f;
8276                     return false;
8277                 }
8278                 return true;
8279             });
8280         }
8281         return field || null;
8282     },
8283      /**
8284      * Mark fields in this form invalid in bulk.
8285      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8286      * @return {BasicForm} this
8287      */
8288     markInvalid : function(errors){
8289         if(errors instanceof Array){
8290             for(var i = 0, len = errors.length; i < len; i++){
8291                 var fieldError = errors[i];
8292                 var f = this.findField(fieldError.id);
8293                 if(f){
8294                     f.markInvalid(fieldError.msg);
8295                 }
8296             }
8297         }else{
8298             var field, id;
8299             for(id in errors){
8300                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8301                     field.markInvalid(errors[id]);
8302                 }
8303             }
8304         }
8305         //Roo.each(this.childForms || [], function (f) {
8306         //    f.markInvalid(errors);
8307         //});
8308
8309         return this;
8310     },
8311
8312     /**
8313      * Set values for fields in this form in bulk.
8314      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8315      * @return {BasicForm} this
8316      */
8317     setValues : function(values){
8318         if(values instanceof Array){ // array of objects
8319             for(var i = 0, len = values.length; i < len; i++){
8320                 var v = values[i];
8321                 var f = this.findField(v.id);
8322                 if(f){
8323                     f.setValue(v.value);
8324                     if(this.trackResetOnLoad){
8325                         f.originalValue = f.getValue();
8326                     }
8327                 }
8328             }
8329         }else{ // object hash
8330             var field, id;
8331             for(id in values){
8332                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8333
8334                     if (field.setFromData &&
8335                         field.valueField &&
8336                         field.displayField &&
8337                         // combos' with local stores can
8338                         // be queried via setValue()
8339                         // to set their value..
8340                         (field.store && !field.store.isLocal)
8341                         ) {
8342                         // it's a combo
8343                         var sd = { };
8344                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8345                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8346                         field.setFromData(sd);
8347
8348                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8349                         
8350                         field.setFromData(values);
8351                         
8352                     } else {
8353                         field.setValue(values[id]);
8354                     }
8355
8356
8357                     if(this.trackResetOnLoad){
8358                         field.originalValue = field.getValue();
8359                     }
8360                 }
8361             }
8362         }
8363
8364         //Roo.each(this.childForms || [], function (f) {
8365         //    f.setValues(values);
8366         //});
8367
8368         return this;
8369     },
8370
8371     /**
8372      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8373      * they are returned as an array.
8374      * @param {Boolean} asString
8375      * @return {Object}
8376      */
8377     getValues : function(asString){
8378         //if (this.childForms) {
8379             // copy values from the child forms
8380         //    Roo.each(this.childForms, function (f) {
8381         //        this.setValues(f.getValues());
8382         //    }, this);
8383         //}
8384
8385
8386
8387         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8388         if(asString === true){
8389             return fs;
8390         }
8391         return Roo.urlDecode(fs);
8392     },
8393
8394     /**
8395      * Returns the fields in this form as an object with key/value pairs.
8396      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8397      * @return {Object}
8398      */
8399     getFieldValues : function(with_hidden)
8400     {
8401         var items = this.getItems();
8402         var ret = {};
8403         items.each(function(f){
8404             
8405             if (!f.getName()) {
8406                 return;
8407             }
8408             
8409             var v = f.getValue();
8410             
8411             if (f.inputType =='radio') {
8412                 if (typeof(ret[f.getName()]) == 'undefined') {
8413                     ret[f.getName()] = ''; // empty..
8414                 }
8415
8416                 if (!f.el.dom.checked) {
8417                     return;
8418
8419                 }
8420                 v = f.el.dom.value;
8421
8422             }
8423             
8424             if(f.xtype == 'MoneyField'){
8425                 ret[f.currencyName] = f.getCurrency();
8426             }
8427
8428             // not sure if this supported any more..
8429             if ((typeof(v) == 'object') && f.getRawValue) {
8430                 v = f.getRawValue() ; // dates..
8431             }
8432             // combo boxes where name != hiddenName...
8433             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8434                 ret[f.name] = f.getRawValue();
8435             }
8436             ret[f.getName()] = v;
8437         });
8438
8439         return ret;
8440     },
8441
8442     /**
8443      * Clears all invalid messages in this form.
8444      * @return {BasicForm} this
8445      */
8446     clearInvalid : function(){
8447         var items = this.getItems();
8448
8449         items.each(function(f){
8450            f.clearInvalid();
8451         });
8452
8453         return this;
8454     },
8455
8456     /**
8457      * Resets this form.
8458      * @return {BasicForm} this
8459      */
8460     reset : function(){
8461         var items = this.getItems();
8462         items.each(function(f){
8463             f.reset();
8464         });
8465
8466         Roo.each(this.childForms || [], function (f) {
8467             f.reset();
8468         });
8469
8470
8471         return this;
8472     },
8473     
8474     getItems : function()
8475     {
8476         var r=new Roo.util.MixedCollection(false, function(o){
8477             return o.id || (o.id = Roo.id());
8478         });
8479         var iter = function(el) {
8480             if (el.inputEl) {
8481                 r.add(el);
8482             }
8483             if (!el.items) {
8484                 return;
8485             }
8486             Roo.each(el.items,function(e) {
8487                 iter(e);
8488             });
8489         };
8490
8491         iter(this);
8492         return r;
8493     },
8494     
8495     hideFields : function(items)
8496     {
8497         Roo.each(items, function(i){
8498             
8499             var f = this.findField(i);
8500             
8501             if(!f){
8502                 return;
8503             }
8504             
8505             f.hide();
8506             
8507         }, this);
8508     },
8509     
8510     showFields : function(items)
8511     {
8512         Roo.each(items, function(i){
8513             
8514             var f = this.findField(i);
8515             
8516             if(!f){
8517                 return;
8518             }
8519             
8520             f.show();
8521             
8522         }, this);
8523     }
8524
8525 });
8526
8527 Roo.apply(Roo.bootstrap.Form, {
8528     
8529     popover : {
8530         
8531         padding : 5,
8532         
8533         isApplied : false,
8534         
8535         isMasked : false,
8536         
8537         form : false,
8538         
8539         target : false,
8540         
8541         toolTip : false,
8542         
8543         intervalID : false,
8544         
8545         maskEl : false,
8546         
8547         apply : function()
8548         {
8549             if(this.isApplied){
8550                 return;
8551             }
8552             
8553             this.maskEl = {
8554                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8555                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8556                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8557                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8558             };
8559             
8560             this.maskEl.top.enableDisplayMode("block");
8561             this.maskEl.left.enableDisplayMode("block");
8562             this.maskEl.bottom.enableDisplayMode("block");
8563             this.maskEl.right.enableDisplayMode("block");
8564             
8565             this.toolTip = new Roo.bootstrap.Tooltip({
8566                 cls : 'roo-form-error-popover',
8567                 alignment : {
8568                     'left' : ['r-l', [-2,0], 'right'],
8569                     'right' : ['l-r', [2,0], 'left'],
8570                     'bottom' : ['tl-bl', [0,2], 'top'],
8571                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8572                 }
8573             });
8574             
8575             this.toolTip.render(Roo.get(document.body));
8576
8577             this.toolTip.el.enableDisplayMode("block");
8578             
8579             Roo.get(document.body).on('click', function(){
8580                 this.unmask();
8581             }, this);
8582             
8583             Roo.get(document.body).on('touchstart', function(){
8584                 this.unmask();
8585             }, this);
8586             
8587             this.isApplied = true
8588         },
8589         
8590         mask : function(form, target)
8591         {
8592             this.form = form;
8593             
8594             this.target = target;
8595             
8596             if(!this.form.errorMask || !target.el){
8597                 return;
8598             }
8599             
8600             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8601             
8602             Roo.log(scrollable);
8603             
8604             var ot = this.target.el.calcOffsetsTo(scrollable);
8605             
8606             var scrollTo = ot[1] - this.form.maskOffset;
8607             
8608             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8609             
8610             scrollable.scrollTo('top', scrollTo);
8611             
8612             var box = this.target.el.getBox();
8613             Roo.log(box);
8614             var zIndex = Roo.bootstrap.Modal.zIndex++;
8615
8616             
8617             this.maskEl.top.setStyle('position', 'absolute');
8618             this.maskEl.top.setStyle('z-index', zIndex);
8619             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8620             this.maskEl.top.setLeft(0);
8621             this.maskEl.top.setTop(0);
8622             this.maskEl.top.show();
8623             
8624             this.maskEl.left.setStyle('position', 'absolute');
8625             this.maskEl.left.setStyle('z-index', zIndex);
8626             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8627             this.maskEl.left.setLeft(0);
8628             this.maskEl.left.setTop(box.y - this.padding);
8629             this.maskEl.left.show();
8630
8631             this.maskEl.bottom.setStyle('position', 'absolute');
8632             this.maskEl.bottom.setStyle('z-index', zIndex);
8633             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8634             this.maskEl.bottom.setLeft(0);
8635             this.maskEl.bottom.setTop(box.bottom + this.padding);
8636             this.maskEl.bottom.show();
8637
8638             this.maskEl.right.setStyle('position', 'absolute');
8639             this.maskEl.right.setStyle('z-index', zIndex);
8640             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8641             this.maskEl.right.setLeft(box.right + this.padding);
8642             this.maskEl.right.setTop(box.y - this.padding);
8643             this.maskEl.right.show();
8644
8645             this.toolTip.bindEl = this.target.el;
8646
8647             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8648
8649             var tip = this.target.blankText;
8650
8651             if(this.target.getValue() !== '' ) {
8652                 
8653                 if (this.target.invalidText.length) {
8654                     tip = this.target.invalidText;
8655                 } else if (this.target.regexText.length){
8656                     tip = this.target.regexText;
8657                 }
8658             }
8659
8660             this.toolTip.show(tip);
8661
8662             this.intervalID = window.setInterval(function() {
8663                 Roo.bootstrap.Form.popover.unmask();
8664             }, 10000);
8665
8666             window.onwheel = function(){ return false;};
8667             
8668             (function(){ this.isMasked = true; }).defer(500, this);
8669             
8670         },
8671         
8672         unmask : function()
8673         {
8674             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8675                 return;
8676             }
8677             
8678             this.maskEl.top.setStyle('position', 'absolute');
8679             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8680             this.maskEl.top.hide();
8681
8682             this.maskEl.left.setStyle('position', 'absolute');
8683             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8684             this.maskEl.left.hide();
8685
8686             this.maskEl.bottom.setStyle('position', 'absolute');
8687             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8688             this.maskEl.bottom.hide();
8689
8690             this.maskEl.right.setStyle('position', 'absolute');
8691             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8692             this.maskEl.right.hide();
8693             
8694             this.toolTip.hide();
8695             
8696             this.toolTip.el.hide();
8697             
8698             window.onwheel = function(){ return true;};
8699             
8700             if(this.intervalID){
8701                 window.clearInterval(this.intervalID);
8702                 this.intervalID = false;
8703             }
8704             
8705             this.isMasked = false;
8706             
8707         }
8708         
8709     }
8710     
8711 });
8712
8713 /*
8714  * Based on:
8715  * Ext JS Library 1.1.1
8716  * Copyright(c) 2006-2007, Ext JS, LLC.
8717  *
8718  * Originally Released Under LGPL - original licence link has changed is not relivant.
8719  *
8720  * Fork - LGPL
8721  * <script type="text/javascript">
8722  */
8723 /**
8724  * @class Roo.form.VTypes
8725  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8726  * @singleton
8727  */
8728 Roo.form.VTypes = function(){
8729     // closure these in so they are only created once.
8730     var alpha = /^[a-zA-Z_]+$/;
8731     var alphanum = /^[a-zA-Z0-9_]+$/;
8732     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8733     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8734
8735     // All these messages and functions are configurable
8736     return {
8737         /**
8738          * The function used to validate email addresses
8739          * @param {String} value The email address
8740          */
8741         'email' : function(v){
8742             return email.test(v);
8743         },
8744         /**
8745          * The error text to display when the email validation function returns false
8746          * @type String
8747          */
8748         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8749         /**
8750          * The keystroke filter mask to be applied on email input
8751          * @type RegExp
8752          */
8753         'emailMask' : /[a-z0-9_\.\-@]/i,
8754
8755         /**
8756          * The function used to validate URLs
8757          * @param {String} value The URL
8758          */
8759         'url' : function(v){
8760             return url.test(v);
8761         },
8762         /**
8763          * The error text to display when the url validation function returns false
8764          * @type String
8765          */
8766         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8767         
8768         /**
8769          * The function used to validate alpha values
8770          * @param {String} value The value
8771          */
8772         'alpha' : function(v){
8773             return alpha.test(v);
8774         },
8775         /**
8776          * The error text to display when the alpha validation function returns false
8777          * @type String
8778          */
8779         'alphaText' : 'This field should only contain letters and _',
8780         /**
8781          * The keystroke filter mask to be applied on alpha input
8782          * @type RegExp
8783          */
8784         'alphaMask' : /[a-z_]/i,
8785
8786         /**
8787          * The function used to validate alphanumeric values
8788          * @param {String} value The value
8789          */
8790         'alphanum' : function(v){
8791             return alphanum.test(v);
8792         },
8793         /**
8794          * The error text to display when the alphanumeric validation function returns false
8795          * @type String
8796          */
8797         'alphanumText' : 'This field should only contain letters, numbers and _',
8798         /**
8799          * The keystroke filter mask to be applied on alphanumeric input
8800          * @type RegExp
8801          */
8802         'alphanumMask' : /[a-z0-9_]/i
8803     };
8804 }();/*
8805  * - LGPL
8806  *
8807  * Input
8808  * 
8809  */
8810
8811 /**
8812  * @class Roo.bootstrap.Input
8813  * @extends Roo.bootstrap.Component
8814  * Bootstrap Input class
8815  * @cfg {Boolean} disabled is it disabled
8816  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8817  * @cfg {String} name name of the input
8818  * @cfg {string} fieldLabel - the label associated
8819  * @cfg {string} placeholder - placeholder to put in text.
8820  * @cfg {string}  before - input group add on before
8821  * @cfg {string} after - input group add on after
8822  * @cfg {string} size - (lg|sm) or leave empty..
8823  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8824  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8825  * @cfg {Number} md colspan out of 12 for computer-sized screens
8826  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8827  * @cfg {string} value default value of the input
8828  * @cfg {Number} labelWidth set the width of label 
8829  * @cfg {Number} labellg set the width of label (1-12)
8830  * @cfg {Number} labelmd set the width of label (1-12)
8831  * @cfg {Number} labelsm set the width of label (1-12)
8832  * @cfg {Number} labelxs set the width of label (1-12)
8833  * @cfg {String} labelAlign (top|left)
8834  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8835  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8836  * @cfg {String} indicatorpos (left|right) default left
8837  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8838  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8839
8840  * @cfg {String} align (left|center|right) Default left
8841  * @cfg {Boolean} forceFeedback (true|false) Default false
8842  * 
8843  * @constructor
8844  * Create a new Input
8845  * @param {Object} config The config object
8846  */
8847
8848 Roo.bootstrap.Input = function(config){
8849     
8850     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8851     
8852     this.addEvents({
8853         /**
8854          * @event focus
8855          * Fires when this field receives input focus.
8856          * @param {Roo.form.Field} this
8857          */
8858         focus : true,
8859         /**
8860          * @event blur
8861          * Fires when this field loses input focus.
8862          * @param {Roo.form.Field} this
8863          */
8864         blur : true,
8865         /**
8866          * @event specialkey
8867          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8868          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8869          * @param {Roo.form.Field} this
8870          * @param {Roo.EventObject} e The event object
8871          */
8872         specialkey : true,
8873         /**
8874          * @event change
8875          * Fires just before the field blurs if the field value has changed.
8876          * @param {Roo.form.Field} this
8877          * @param {Mixed} newValue The new value
8878          * @param {Mixed} oldValue The original value
8879          */
8880         change : true,
8881         /**
8882          * @event invalid
8883          * Fires after the field has been marked as invalid.
8884          * @param {Roo.form.Field} this
8885          * @param {String} msg The validation message
8886          */
8887         invalid : true,
8888         /**
8889          * @event valid
8890          * Fires after the field has been validated with no errors.
8891          * @param {Roo.form.Field} this
8892          */
8893         valid : true,
8894          /**
8895          * @event keyup
8896          * Fires after the key up
8897          * @param {Roo.form.Field} this
8898          * @param {Roo.EventObject}  e The event Object
8899          */
8900         keyup : true
8901     });
8902 };
8903
8904 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8905      /**
8906      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8907       automatic validation (defaults to "keyup").
8908      */
8909     validationEvent : "keyup",
8910      /**
8911      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8912      */
8913     validateOnBlur : true,
8914     /**
8915      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8916      */
8917     validationDelay : 250,
8918      /**
8919      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8920      */
8921     focusClass : "x-form-focus",  // not needed???
8922     
8923        
8924     /**
8925      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8926      */
8927     invalidClass : "has-warning",
8928     
8929     /**
8930      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8931      */
8932     validClass : "has-success",
8933     
8934     /**
8935      * @cfg {Boolean} hasFeedback (true|false) default true
8936      */
8937     hasFeedback : true,
8938     
8939     /**
8940      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8941      */
8942     invalidFeedbackClass : "glyphicon-warning-sign",
8943     
8944     /**
8945      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8946      */
8947     validFeedbackClass : "glyphicon-ok",
8948     
8949     /**
8950      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8951      */
8952     selectOnFocus : false,
8953     
8954      /**
8955      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8956      */
8957     maskRe : null,
8958        /**
8959      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8960      */
8961     vtype : null,
8962     
8963       /**
8964      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8965      */
8966     disableKeyFilter : false,
8967     
8968        /**
8969      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8970      */
8971     disabled : false,
8972      /**
8973      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8974      */
8975     allowBlank : true,
8976     /**
8977      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8978      */
8979     blankText : "Please complete this mandatory field",
8980     
8981      /**
8982      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8983      */
8984     minLength : 0,
8985     /**
8986      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8987      */
8988     maxLength : Number.MAX_VALUE,
8989     /**
8990      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8991      */
8992     minLengthText : "The minimum length for this field is {0}",
8993     /**
8994      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8995      */
8996     maxLengthText : "The maximum length for this field is {0}",
8997   
8998     
8999     /**
9000      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9001      * If available, this function will be called only after the basic validators all return true, and will be passed the
9002      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9003      */
9004     validator : null,
9005     /**
9006      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9007      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9008      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9009      */
9010     regex : null,
9011     /**
9012      * @cfg {String} regexText -- Depricated - use Invalid Text
9013      */
9014     regexText : "",
9015     
9016     /**
9017      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9018      */
9019     invalidText : "",
9020     
9021     
9022     
9023     autocomplete: false,
9024     
9025     
9026     fieldLabel : '',
9027     inputType : 'text',
9028     
9029     name : false,
9030     placeholder: false,
9031     before : false,
9032     after : false,
9033     size : false,
9034     hasFocus : false,
9035     preventMark: false,
9036     isFormField : true,
9037     value : '',
9038     labelWidth : 2,
9039     labelAlign : false,
9040     readOnly : false,
9041     align : false,
9042     formatedValue : false,
9043     forceFeedback : false,
9044     
9045     indicatorpos : 'left',
9046     
9047     labellg : 0,
9048     labelmd : 0,
9049     labelsm : 0,
9050     labelxs : 0,
9051     
9052     capture : '',
9053     accept : '',
9054     
9055     parentLabelAlign : function()
9056     {
9057         var parent = this;
9058         while (parent.parent()) {
9059             parent = parent.parent();
9060             if (typeof(parent.labelAlign) !='undefined') {
9061                 return parent.labelAlign;
9062             }
9063         }
9064         return 'left';
9065         
9066     },
9067     
9068     getAutoCreate : function()
9069     {
9070         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9071         
9072         var id = Roo.id();
9073         
9074         var cfg = {};
9075         
9076         if(this.inputType != 'hidden'){
9077             cfg.cls = 'form-group' //input-group
9078         }
9079         
9080         var input =  {
9081             tag: 'input',
9082             id : id,
9083             type : this.inputType,
9084             value : this.value,
9085             cls : 'form-control',
9086             placeholder : this.placeholder || '',
9087             autocomplete : this.autocomplete || 'new-password'
9088         };
9089         
9090         if(this.capture.length){
9091             input.capture = this.capture;
9092         }
9093         
9094         if(this.accept.length){
9095             input.accept = this.accept + "/*";
9096         }
9097         
9098         if(this.align){
9099             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9100         }
9101         
9102         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9103             input.maxLength = this.maxLength;
9104         }
9105         
9106         if (this.disabled) {
9107             input.disabled=true;
9108         }
9109         
9110         if (this.readOnly) {
9111             input.readonly=true;
9112         }
9113         
9114         if (this.name) {
9115             input.name = this.name;
9116         }
9117         
9118         if (this.size) {
9119             input.cls += ' input-' + this.size;
9120         }
9121         
9122         var settings=this;
9123         ['xs','sm','md','lg'].map(function(size){
9124             if (settings[size]) {
9125                 cfg.cls += ' col-' + size + '-' + settings[size];
9126             }
9127         });
9128         
9129         var inputblock = input;
9130         
9131         var feedback = {
9132             tag: 'span',
9133             cls: 'glyphicon form-control-feedback'
9134         };
9135             
9136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9137             
9138             inputblock = {
9139                 cls : 'has-feedback',
9140                 cn :  [
9141                     input,
9142                     feedback
9143                 ] 
9144             };  
9145         }
9146         
9147         if (this.before || this.after) {
9148             
9149             inputblock = {
9150                 cls : 'input-group',
9151                 cn :  [] 
9152             };
9153             
9154             if (this.before && typeof(this.before) == 'string') {
9155                 
9156                 inputblock.cn.push({
9157                     tag :'span',
9158                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9159                     html : this.before
9160                 });
9161             }
9162             if (this.before && typeof(this.before) == 'object') {
9163                 this.before = Roo.factory(this.before);
9164                 
9165                 inputblock.cn.push({
9166                     tag :'span',
9167                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9168                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9169                 });
9170             }
9171             
9172             inputblock.cn.push(input);
9173             
9174             if (this.after && typeof(this.after) == 'string') {
9175                 inputblock.cn.push({
9176                     tag :'span',
9177                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9178                     html : this.after
9179                 });
9180             }
9181             if (this.after && typeof(this.after) == 'object') {
9182                 this.after = Roo.factory(this.after);
9183                 
9184                 inputblock.cn.push({
9185                     tag :'span',
9186                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9187                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9188                 });
9189             }
9190             
9191             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9192                 inputblock.cls += ' has-feedback';
9193                 inputblock.cn.push(feedback);
9194             }
9195         };
9196         var indicator = {
9197             tag : 'i',
9198             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9199             tooltip : 'This field is required'
9200         };
9201         if (Roo.bootstrap.version == 4) {
9202             indicator = {
9203                 tag : 'i',
9204                 style : 'display-none'
9205             };
9206         }
9207         if (align ==='left' && this.fieldLabel.length) {
9208             
9209             cfg.cls += ' roo-form-group-label-left row';
9210             
9211             cfg.cn = [
9212                 indicator,
9213                 {
9214                     tag: 'label',
9215                     'for' :  id,
9216                     cls : 'control-label col-form-label',
9217                     html : this.fieldLabel
9218
9219                 },
9220                 {
9221                     cls : "", 
9222                     cn: [
9223                         inputblock
9224                     ]
9225                 }
9226             ];
9227             
9228             var labelCfg = cfg.cn[1];
9229             var contentCfg = cfg.cn[2];
9230             
9231             if(this.indicatorpos == 'right'){
9232                 cfg.cn = [
9233                     {
9234                         tag: 'label',
9235                         'for' :  id,
9236                         cls : 'control-label col-form-label',
9237                         cn : [
9238                             {
9239                                 tag : 'span',
9240                                 html : this.fieldLabel
9241                             },
9242                             indicator
9243                         ]
9244                     },
9245                     {
9246                         cls : "",
9247                         cn: [
9248                             inputblock
9249                         ]
9250                     }
9251
9252                 ];
9253                 
9254                 labelCfg = cfg.cn[0];
9255                 contentCfg = cfg.cn[1];
9256             
9257             }
9258             
9259             if(this.labelWidth > 12){
9260                 labelCfg.style = "width: " + this.labelWidth + 'px';
9261             }
9262             
9263             if(this.labelWidth < 13 && this.labelmd == 0){
9264                 this.labelmd = this.labelWidth;
9265             }
9266             
9267             if(this.labellg > 0){
9268                 labelCfg.cls += ' col-lg-' + this.labellg;
9269                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9270             }
9271             
9272             if(this.labelmd > 0){
9273                 labelCfg.cls += ' col-md-' + this.labelmd;
9274                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9275             }
9276             
9277             if(this.labelsm > 0){
9278                 labelCfg.cls += ' col-sm-' + this.labelsm;
9279                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9280             }
9281             
9282             if(this.labelxs > 0){
9283                 labelCfg.cls += ' col-xs-' + this.labelxs;
9284                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9285             }
9286             
9287             
9288         } else if ( this.fieldLabel.length) {
9289                 
9290             cfg.cn = [
9291                 {
9292                     tag : 'i',
9293                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9294                     tooltip : 'This field is required'
9295                 },
9296                 {
9297                     tag: 'label',
9298                    //cls : 'input-group-addon',
9299                     html : this.fieldLabel
9300
9301                 },
9302
9303                inputblock
9304
9305            ];
9306            
9307            if(this.indicatorpos == 'right'){
9308                 
9309                 cfg.cn = [
9310                     {
9311                         tag: 'label',
9312                        //cls : 'input-group-addon',
9313                         html : this.fieldLabel
9314
9315                     },
9316                     {
9317                         tag : 'i',
9318                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9319                         tooltip : 'This field is required'
9320                     },
9321
9322                    inputblock
9323
9324                ];
9325
9326             }
9327
9328         } else {
9329             
9330             cfg.cn = [
9331
9332                     inputblock
9333
9334             ];
9335                 
9336                 
9337         };
9338         
9339         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9340            cfg.cls += ' navbar-form';
9341         }
9342         
9343         if (this.parentType === 'NavGroup') {
9344            cfg.cls += ' navbar-form';
9345            cfg.tag = 'li';
9346         }
9347         
9348         return cfg;
9349         
9350     },
9351     /**
9352      * return the real input element.
9353      */
9354     inputEl: function ()
9355     {
9356         return this.el.select('input.form-control',true).first();
9357     },
9358     
9359     tooltipEl : function()
9360     {
9361         return this.inputEl();
9362     },
9363     
9364     indicatorEl : function()
9365     {
9366         if (Roo.bootstrap.version == 4) {
9367             return false; // not enabled in v4 yet.
9368         }
9369         
9370         var indicator = this.el.select('i.roo-required-indicator',true).first();
9371         
9372         if(!indicator){
9373             return false;
9374         }
9375         
9376         return indicator;
9377         
9378     },
9379     
9380     setDisabled : function(v)
9381     {
9382         var i  = this.inputEl().dom;
9383         if (!v) {
9384             i.removeAttribute('disabled');
9385             return;
9386             
9387         }
9388         i.setAttribute('disabled','true');
9389     },
9390     initEvents : function()
9391     {
9392           
9393         this.inputEl().on("keydown" , this.fireKey,  this);
9394         this.inputEl().on("focus", this.onFocus,  this);
9395         this.inputEl().on("blur", this.onBlur,  this);
9396         
9397         this.inputEl().relayEvent('keyup', this);
9398         
9399         this.indicator = this.indicatorEl();
9400         
9401         if(this.indicator){
9402             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9403         }
9404  
9405         // reference to original value for reset
9406         this.originalValue = this.getValue();
9407         //Roo.form.TextField.superclass.initEvents.call(this);
9408         if(this.validationEvent == 'keyup'){
9409             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9410             this.inputEl().on('keyup', this.filterValidation, this);
9411         }
9412         else if(this.validationEvent !== false){
9413             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9414         }
9415         
9416         if(this.selectOnFocus){
9417             this.on("focus", this.preFocus, this);
9418             
9419         }
9420         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9421             this.inputEl().on("keypress", this.filterKeys, this);
9422         } else {
9423             this.inputEl().relayEvent('keypress', this);
9424         }
9425        /* if(this.grow){
9426             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9427             this.el.on("click", this.autoSize,  this);
9428         }
9429         */
9430         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9431             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9432         }
9433         
9434         if (typeof(this.before) == 'object') {
9435             this.before.render(this.el.select('.roo-input-before',true).first());
9436         }
9437         if (typeof(this.after) == 'object') {
9438             this.after.render(this.el.select('.roo-input-after',true).first());
9439         }
9440         
9441         this.inputEl().on('change', this.onChange, this);
9442         
9443     },
9444     filterValidation : function(e){
9445         if(!e.isNavKeyPress()){
9446             this.validationTask.delay(this.validationDelay);
9447         }
9448     },
9449      /**
9450      * Validates the field value
9451      * @return {Boolean} True if the value is valid, else false
9452      */
9453     validate : function(){
9454         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9455         if(this.disabled || this.validateValue(this.getRawValue())){
9456             this.markValid();
9457             return true;
9458         }
9459         
9460         this.markInvalid();
9461         return false;
9462     },
9463     
9464     
9465     /**
9466      * Validates a value according to the field's validation rules and marks the field as invalid
9467      * if the validation fails
9468      * @param {Mixed} value The value to validate
9469      * @return {Boolean} True if the value is valid, else false
9470      */
9471     validateValue : function(value)
9472     {
9473         if(this.getVisibilityEl().hasClass('hidden')){
9474             return true;
9475         }
9476         
9477         if(value.length < 1)  { // if it's blank
9478             if(this.allowBlank){
9479                 return true;
9480             }
9481             return false;
9482         }
9483         
9484         if(value.length < this.minLength){
9485             return false;
9486         }
9487         if(value.length > this.maxLength){
9488             return false;
9489         }
9490         if(this.vtype){
9491             var vt = Roo.form.VTypes;
9492             if(!vt[this.vtype](value, this)){
9493                 return false;
9494             }
9495         }
9496         if(typeof this.validator == "function"){
9497             var msg = this.validator(value);
9498             if(msg !== true){
9499                 return false;
9500             }
9501             if (typeof(msg) == 'string') {
9502                 this.invalidText = msg;
9503             }
9504         }
9505         
9506         if(this.regex && !this.regex.test(value)){
9507             return false;
9508         }
9509         
9510         return true;
9511     },
9512     
9513      // private
9514     fireKey : function(e){
9515         //Roo.log('field ' + e.getKey());
9516         if(e.isNavKeyPress()){
9517             this.fireEvent("specialkey", this, e);
9518         }
9519     },
9520     focus : function (selectText){
9521         if(this.rendered){
9522             this.inputEl().focus();
9523             if(selectText === true){
9524                 this.inputEl().dom.select();
9525             }
9526         }
9527         return this;
9528     } ,
9529     
9530     onFocus : function(){
9531         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9532            // this.el.addClass(this.focusClass);
9533         }
9534         if(!this.hasFocus){
9535             this.hasFocus = true;
9536             this.startValue = this.getValue();
9537             this.fireEvent("focus", this);
9538         }
9539     },
9540     
9541     beforeBlur : Roo.emptyFn,
9542
9543     
9544     // private
9545     onBlur : function(){
9546         this.beforeBlur();
9547         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9548             //this.el.removeClass(this.focusClass);
9549         }
9550         this.hasFocus = false;
9551         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9552             this.validate();
9553         }
9554         var v = this.getValue();
9555         if(String(v) !== String(this.startValue)){
9556             this.fireEvent('change', this, v, this.startValue);
9557         }
9558         this.fireEvent("blur", this);
9559     },
9560     
9561     onChange : function(e)
9562     {
9563         var v = this.getValue();
9564         if(String(v) !== String(this.startValue)){
9565             this.fireEvent('change', this, v, this.startValue);
9566         }
9567         
9568     },
9569     
9570     /**
9571      * Resets the current field value to the originally loaded value and clears any validation messages
9572      */
9573     reset : function(){
9574         this.setValue(this.originalValue);
9575         this.validate();
9576     },
9577      /**
9578      * Returns the name of the field
9579      * @return {Mixed} name The name field
9580      */
9581     getName: function(){
9582         return this.name;
9583     },
9584      /**
9585      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9586      * @return {Mixed} value The field value
9587      */
9588     getValue : function(){
9589         
9590         var v = this.inputEl().getValue();
9591         
9592         return v;
9593     },
9594     /**
9595      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9596      * @return {Mixed} value The field value
9597      */
9598     getRawValue : function(){
9599         var v = this.inputEl().getValue();
9600         
9601         return v;
9602     },
9603     
9604     /**
9605      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9606      * @param {Mixed} value The value to set
9607      */
9608     setRawValue : function(v){
9609         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9610     },
9611     
9612     selectText : function(start, end){
9613         var v = this.getRawValue();
9614         if(v.length > 0){
9615             start = start === undefined ? 0 : start;
9616             end = end === undefined ? v.length : end;
9617             var d = this.inputEl().dom;
9618             if(d.setSelectionRange){
9619                 d.setSelectionRange(start, end);
9620             }else if(d.createTextRange){
9621                 var range = d.createTextRange();
9622                 range.moveStart("character", start);
9623                 range.moveEnd("character", v.length-end);
9624                 range.select();
9625             }
9626         }
9627     },
9628     
9629     /**
9630      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9631      * @param {Mixed} value The value to set
9632      */
9633     setValue : function(v){
9634         this.value = v;
9635         if(this.rendered){
9636             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9637             this.validate();
9638         }
9639     },
9640     
9641     /*
9642     processValue : function(value){
9643         if(this.stripCharsRe){
9644             var newValue = value.replace(this.stripCharsRe, '');
9645             if(newValue !== value){
9646                 this.setRawValue(newValue);
9647                 return newValue;
9648             }
9649         }
9650         return value;
9651     },
9652   */
9653     preFocus : function(){
9654         
9655         if(this.selectOnFocus){
9656             this.inputEl().dom.select();
9657         }
9658     },
9659     filterKeys : function(e){
9660         var k = e.getKey();
9661         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9662             return;
9663         }
9664         var c = e.getCharCode(), cc = String.fromCharCode(c);
9665         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9666             return;
9667         }
9668         if(!this.maskRe.test(cc)){
9669             e.stopEvent();
9670         }
9671     },
9672      /**
9673      * Clear any invalid styles/messages for this field
9674      */
9675     clearInvalid : function(){
9676         
9677         if(!this.el || this.preventMark){ // not rendered
9678             return;
9679         }
9680         
9681      
9682         this.el.removeClass(this.invalidClass);
9683         
9684         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9685             
9686             var feedback = this.el.select('.form-control-feedback', true).first();
9687             
9688             if(feedback){
9689                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9690             }
9691             
9692         }
9693         
9694         if(this.indicator){
9695             this.indicator.removeClass('visible');
9696             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9697         }
9698         
9699         this.fireEvent('valid', this);
9700     },
9701     
9702      /**
9703      * Mark this field as valid
9704      */
9705     markValid : function()
9706     {
9707         if(!this.el  || this.preventMark){ // not rendered...
9708             return;
9709         }
9710         
9711         this.el.removeClass([this.invalidClass, this.validClass]);
9712         
9713         var feedback = this.el.select('.form-control-feedback', true).first();
9714             
9715         if(feedback){
9716             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9717         }
9718         
9719         if(this.indicator){
9720             this.indicator.removeClass('visible');
9721             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9722         }
9723         
9724         if(this.disabled){
9725             return;
9726         }
9727         
9728         if(this.allowBlank && !this.getRawValue().length){
9729             return;
9730         }
9731         
9732         this.el.addClass(this.validClass);
9733         
9734         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9735             
9736             var feedback = this.el.select('.form-control-feedback', true).first();
9737             
9738             if(feedback){
9739                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9740                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9741             }
9742             
9743         }
9744         
9745         this.fireEvent('valid', this);
9746     },
9747     
9748      /**
9749      * Mark this field as invalid
9750      * @param {String} msg The validation message
9751      */
9752     markInvalid : function(msg)
9753     {
9754         if(!this.el  || this.preventMark){ // not rendered
9755             return;
9756         }
9757         
9758         this.el.removeClass([this.invalidClass, this.validClass]);
9759         
9760         var feedback = this.el.select('.form-control-feedback', true).first();
9761             
9762         if(feedback){
9763             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9764         }
9765
9766         if(this.disabled){
9767             return;
9768         }
9769         
9770         if(this.allowBlank && !this.getRawValue().length){
9771             return;
9772         }
9773         
9774         if(this.indicator){
9775             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9776             this.indicator.addClass('visible');
9777         }
9778         
9779         this.el.addClass(this.invalidClass);
9780         
9781         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9782             
9783             var feedback = this.el.select('.form-control-feedback', true).first();
9784             
9785             if(feedback){
9786                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9787                 
9788                 if(this.getValue().length || this.forceFeedback){
9789                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9790                 }
9791                 
9792             }
9793             
9794         }
9795         
9796         this.fireEvent('invalid', this, msg);
9797     },
9798     // private
9799     SafariOnKeyDown : function(event)
9800     {
9801         // this is a workaround for a password hang bug on chrome/ webkit.
9802         if (this.inputEl().dom.type != 'password') {
9803             return;
9804         }
9805         
9806         var isSelectAll = false;
9807         
9808         if(this.inputEl().dom.selectionEnd > 0){
9809             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9810         }
9811         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9812             event.preventDefault();
9813             this.setValue('');
9814             return;
9815         }
9816         
9817         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9818             
9819             event.preventDefault();
9820             // this is very hacky as keydown always get's upper case.
9821             //
9822             var cc = String.fromCharCode(event.getCharCode());
9823             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9824             
9825         }
9826     },
9827     adjustWidth : function(tag, w){
9828         tag = tag.toLowerCase();
9829         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9830             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9831                 if(tag == 'input'){
9832                     return w + 2;
9833                 }
9834                 if(tag == 'textarea'){
9835                     return w-2;
9836                 }
9837             }else if(Roo.isOpera){
9838                 if(tag == 'input'){
9839                     return w + 2;
9840                 }
9841                 if(tag == 'textarea'){
9842                     return w-2;
9843                 }
9844             }
9845         }
9846         return w;
9847     },
9848     
9849     setFieldLabel : function(v)
9850     {
9851         if(!this.rendered){
9852             return;
9853         }
9854         
9855         if(this.indicatorEl()){
9856             var ar = this.el.select('label > span',true);
9857             
9858             if (ar.elements.length) {
9859                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9860                 this.fieldLabel = v;
9861                 return;
9862             }
9863             
9864             var br = this.el.select('label',true);
9865             
9866             if(br.elements.length) {
9867                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9868                 this.fieldLabel = v;
9869                 return;
9870             }
9871             
9872             Roo.log('Cannot Found any of label > span || label in input');
9873             return;
9874         }
9875         
9876         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9877         this.fieldLabel = v;
9878         
9879         
9880     }
9881 });
9882
9883  
9884 /*
9885  * - LGPL
9886  *
9887  * Input
9888  * 
9889  */
9890
9891 /**
9892  * @class Roo.bootstrap.TextArea
9893  * @extends Roo.bootstrap.Input
9894  * Bootstrap TextArea class
9895  * @cfg {Number} cols Specifies the visible width of a text area
9896  * @cfg {Number} rows Specifies the visible number of lines in a text area
9897  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9898  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9899  * @cfg {string} html text
9900  * 
9901  * @constructor
9902  * Create a new TextArea
9903  * @param {Object} config The config object
9904  */
9905
9906 Roo.bootstrap.TextArea = function(config){
9907     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9908    
9909 };
9910
9911 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9912      
9913     cols : false,
9914     rows : 5,
9915     readOnly : false,
9916     warp : 'soft',
9917     resize : false,
9918     value: false,
9919     html: false,
9920     
9921     getAutoCreate : function(){
9922         
9923         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9924         
9925         var id = Roo.id();
9926         
9927         var cfg = {};
9928         
9929         if(this.inputType != 'hidden'){
9930             cfg.cls = 'form-group' //input-group
9931         }
9932         
9933         var input =  {
9934             tag: 'textarea',
9935             id : id,
9936             warp : this.warp,
9937             rows : this.rows,
9938             value : this.value || '',
9939             html: this.html || '',
9940             cls : 'form-control',
9941             placeholder : this.placeholder || '' 
9942             
9943         };
9944         
9945         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9946             input.maxLength = this.maxLength;
9947         }
9948         
9949         if(this.resize){
9950             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9951         }
9952         
9953         if(this.cols){
9954             input.cols = this.cols;
9955         }
9956         
9957         if (this.readOnly) {
9958             input.readonly = true;
9959         }
9960         
9961         if (this.name) {
9962             input.name = this.name;
9963         }
9964         
9965         if (this.size) {
9966             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9967         }
9968         
9969         var settings=this;
9970         ['xs','sm','md','lg'].map(function(size){
9971             if (settings[size]) {
9972                 cfg.cls += ' col-' + size + '-' + settings[size];
9973             }
9974         });
9975         
9976         var inputblock = input;
9977         
9978         if(this.hasFeedback && !this.allowBlank){
9979             
9980             var feedback = {
9981                 tag: 'span',
9982                 cls: 'glyphicon form-control-feedback'
9983             };
9984
9985             inputblock = {
9986                 cls : 'has-feedback',
9987                 cn :  [
9988                     input,
9989                     feedback
9990                 ] 
9991             };  
9992         }
9993         
9994         
9995         if (this.before || this.after) {
9996             
9997             inputblock = {
9998                 cls : 'input-group',
9999                 cn :  [] 
10000             };
10001             if (this.before) {
10002                 inputblock.cn.push({
10003                     tag :'span',
10004                     cls : 'input-group-addon',
10005                     html : this.before
10006                 });
10007             }
10008             
10009             inputblock.cn.push(input);
10010             
10011             if(this.hasFeedback && !this.allowBlank){
10012                 inputblock.cls += ' has-feedback';
10013                 inputblock.cn.push(feedback);
10014             }
10015             
10016             if (this.after) {
10017                 inputblock.cn.push({
10018                     tag :'span',
10019                     cls : 'input-group-addon',
10020                     html : this.after
10021                 });
10022             }
10023             
10024         }
10025         
10026         if (align ==='left' && this.fieldLabel.length) {
10027             cfg.cn = [
10028                 {
10029                     tag: 'label',
10030                     'for' :  id,
10031                     cls : 'control-label',
10032                     html : this.fieldLabel
10033                 },
10034                 {
10035                     cls : "",
10036                     cn: [
10037                         inputblock
10038                     ]
10039                 }
10040
10041             ];
10042             
10043             if(this.labelWidth > 12){
10044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10045             }
10046
10047             if(this.labelWidth < 13 && this.labelmd == 0){
10048                 this.labelmd = this.labelWidth;
10049             }
10050
10051             if(this.labellg > 0){
10052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10054             }
10055
10056             if(this.labelmd > 0){
10057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10059             }
10060
10061             if(this.labelsm > 0){
10062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10064             }
10065
10066             if(this.labelxs > 0){
10067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10069             }
10070             
10071         } else if ( this.fieldLabel.length) {
10072             cfg.cn = [
10073
10074                {
10075                    tag: 'label',
10076                    //cls : 'input-group-addon',
10077                    html : this.fieldLabel
10078
10079                },
10080
10081                inputblock
10082
10083            ];
10084
10085         } else {
10086
10087             cfg.cn = [
10088
10089                 inputblock
10090
10091             ];
10092                 
10093         }
10094         
10095         if (this.disabled) {
10096             input.disabled=true;
10097         }
10098         
10099         return cfg;
10100         
10101     },
10102     /**
10103      * return the real textarea element.
10104      */
10105     inputEl: function ()
10106     {
10107         return this.el.select('textarea.form-control',true).first();
10108     },
10109     
10110     /**
10111      * Clear any invalid styles/messages for this field
10112      */
10113     clearInvalid : function()
10114     {
10115         
10116         if(!this.el || this.preventMark){ // not rendered
10117             return;
10118         }
10119         
10120         var label = this.el.select('label', true).first();
10121         var icon = this.el.select('i.fa-star', true).first();
10122         
10123         if(label && icon){
10124             icon.remove();
10125         }
10126         
10127         this.el.removeClass(this.invalidClass);
10128         
10129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10130             
10131             var feedback = this.el.select('.form-control-feedback', true).first();
10132             
10133             if(feedback){
10134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10135             }
10136             
10137         }
10138         
10139         this.fireEvent('valid', this);
10140     },
10141     
10142      /**
10143      * Mark this field as valid
10144      */
10145     markValid : function()
10146     {
10147         if(!this.el  || this.preventMark){ // not rendered
10148             return;
10149         }
10150         
10151         this.el.removeClass([this.invalidClass, this.validClass]);
10152         
10153         var feedback = this.el.select('.form-control-feedback', true).first();
10154             
10155         if(feedback){
10156             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10157         }
10158
10159         if(this.disabled || this.allowBlank){
10160             return;
10161         }
10162         
10163         var label = this.el.select('label', true).first();
10164         var icon = this.el.select('i.fa-star', true).first();
10165         
10166         if(label && icon){
10167             icon.remove();
10168         }
10169         
10170         this.el.addClass(this.validClass);
10171         
10172         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10173             
10174             var feedback = this.el.select('.form-control-feedback', true).first();
10175             
10176             if(feedback){
10177                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10178                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10179             }
10180             
10181         }
10182         
10183         this.fireEvent('valid', this);
10184     },
10185     
10186      /**
10187      * Mark this field as invalid
10188      * @param {String} msg The validation message
10189      */
10190     markInvalid : function(msg)
10191     {
10192         if(!this.el  || this.preventMark){ // not rendered
10193             return;
10194         }
10195         
10196         this.el.removeClass([this.invalidClass, this.validClass]);
10197         
10198         var feedback = this.el.select('.form-control-feedback', true).first();
10199             
10200         if(feedback){
10201             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10202         }
10203
10204         if(this.disabled || this.allowBlank){
10205             return;
10206         }
10207         
10208         var label = this.el.select('label', true).first();
10209         var icon = this.el.select('i.fa-star', true).first();
10210         
10211         if(!this.getValue().length && label && !icon){
10212             this.el.createChild({
10213                 tag : 'i',
10214                 cls : 'text-danger fa fa-lg fa-star',
10215                 tooltip : 'This field is required',
10216                 style : 'margin-right:5px;'
10217             }, label, true);
10218         }
10219
10220         this.el.addClass(this.invalidClass);
10221         
10222         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10223             
10224             var feedback = this.el.select('.form-control-feedback', true).first();
10225             
10226             if(feedback){
10227                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10228                 
10229                 if(this.getValue().length || this.forceFeedback){
10230                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10231                 }
10232                 
10233             }
10234             
10235         }
10236         
10237         this.fireEvent('invalid', this, msg);
10238     }
10239 });
10240
10241  
10242 /*
10243  * - LGPL
10244  *
10245  * trigger field - base class for combo..
10246  * 
10247  */
10248  
10249 /**
10250  * @class Roo.bootstrap.TriggerField
10251  * @extends Roo.bootstrap.Input
10252  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10253  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10254  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10255  * for which you can provide a custom implementation.  For example:
10256  * <pre><code>
10257 var trigger = new Roo.bootstrap.TriggerField();
10258 trigger.onTriggerClick = myTriggerFn;
10259 trigger.applyTo('my-field');
10260 </code></pre>
10261  *
10262  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10263  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10264  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10265  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10266  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10267
10268  * @constructor
10269  * Create a new TriggerField.
10270  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10271  * to the base TextField)
10272  */
10273 Roo.bootstrap.TriggerField = function(config){
10274     this.mimicing = false;
10275     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10276 };
10277
10278 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10279     /**
10280      * @cfg {String} triggerClass A CSS class to apply to the trigger
10281      */
10282      /**
10283      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10284      */
10285     hideTrigger:false,
10286
10287     /**
10288      * @cfg {Boolean} removable (true|false) special filter default false
10289      */
10290     removable : false,
10291     
10292     /** @cfg {Boolean} grow @hide */
10293     /** @cfg {Number} growMin @hide */
10294     /** @cfg {Number} growMax @hide */
10295
10296     /**
10297      * @hide 
10298      * @method
10299      */
10300     autoSize: Roo.emptyFn,
10301     // private
10302     monitorTab : true,
10303     // private
10304     deferHeight : true,
10305
10306     
10307     actionMode : 'wrap',
10308     
10309     caret : false,
10310     
10311     
10312     getAutoCreate : function(){
10313        
10314         var align = this.labelAlign || this.parentLabelAlign();
10315         
10316         var id = Roo.id();
10317         
10318         var cfg = {
10319             cls: 'form-group' //input-group
10320         };
10321         
10322         
10323         var input =  {
10324             tag: 'input',
10325             id : id,
10326             type : this.inputType,
10327             cls : 'form-control',
10328             autocomplete: 'new-password',
10329             placeholder : this.placeholder || '' 
10330             
10331         };
10332         if (this.name) {
10333             input.name = this.name;
10334         }
10335         if (this.size) {
10336             input.cls += ' input-' + this.size;
10337         }
10338         
10339         if (this.disabled) {
10340             input.disabled=true;
10341         }
10342         
10343         var inputblock = input;
10344         
10345         if(this.hasFeedback && !this.allowBlank){
10346             
10347             var feedback = {
10348                 tag: 'span',
10349                 cls: 'glyphicon form-control-feedback'
10350             };
10351             
10352             if(this.removable && !this.editable && !this.tickable){
10353                 inputblock = {
10354                     cls : 'has-feedback',
10355                     cn :  [
10356                         inputblock,
10357                         {
10358                             tag: 'button',
10359                             html : 'x',
10360                             cls : 'roo-combo-removable-btn close'
10361                         },
10362                         feedback
10363                     ] 
10364                 };
10365             } else {
10366                 inputblock = {
10367                     cls : 'has-feedback',
10368                     cn :  [
10369                         inputblock,
10370                         feedback
10371                     ] 
10372                 };
10373             }
10374
10375         } else {
10376             if(this.removable && !this.editable && !this.tickable){
10377                 inputblock = {
10378                     cls : 'roo-removable',
10379                     cn :  [
10380                         inputblock,
10381                         {
10382                             tag: 'button',
10383                             html : 'x',
10384                             cls : 'roo-combo-removable-btn close'
10385                         }
10386                     ] 
10387                 };
10388             }
10389         }
10390         
10391         if (this.before || this.after) {
10392             
10393             inputblock = {
10394                 cls : 'input-group',
10395                 cn :  [] 
10396             };
10397             if (this.before) {
10398                 inputblock.cn.push({
10399                     tag :'span',
10400                     cls : 'input-group-addon input-group-prepend input-group-text',
10401                     html : this.before
10402                 });
10403             }
10404             
10405             inputblock.cn.push(input);
10406             
10407             if(this.hasFeedback && !this.allowBlank){
10408                 inputblock.cls += ' has-feedback';
10409                 inputblock.cn.push(feedback);
10410             }
10411             
10412             if (this.after) {
10413                 inputblock.cn.push({
10414                     tag :'span',
10415                     cls : 'input-group-addon input-group-append input-group-text',
10416                     html : this.after
10417                 });
10418             }
10419             
10420         };
10421         
10422       
10423         
10424         var ibwrap = inputblock;
10425         
10426         if(this.multiple){
10427             ibwrap = {
10428                 tag: 'ul',
10429                 cls: 'roo-select2-choices',
10430                 cn:[
10431                     {
10432                         tag: 'li',
10433                         cls: 'roo-select2-search-field',
10434                         cn: [
10435
10436                             inputblock
10437                         ]
10438                     }
10439                 ]
10440             };
10441                 
10442         }
10443         
10444         var combobox = {
10445             cls: 'roo-select2-container input-group',
10446             cn: [
10447                  {
10448                     tag: 'input',
10449                     type : 'hidden',
10450                     cls: 'form-hidden-field'
10451                 },
10452                 ibwrap
10453             ]
10454         };
10455         
10456         if(!this.multiple && this.showToggleBtn){
10457             
10458             var caret = {
10459                         tag: 'span',
10460                         cls: 'caret'
10461              };
10462             if (this.caret != false) {
10463                 caret = {
10464                      tag: 'i',
10465                      cls: 'fa fa-' + this.caret
10466                 };
10467                 
10468             }
10469             
10470             combobox.cn.push({
10471                 tag :'span',
10472                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10473                 cn : [
10474                     caret,
10475                     {
10476                         tag: 'span',
10477                         cls: 'combobox-clear',
10478                         cn  : [
10479                             {
10480                                 tag : 'i',
10481                                 cls: 'icon-remove'
10482                             }
10483                         ]
10484                     }
10485                 ]
10486
10487             })
10488         }
10489         
10490         if(this.multiple){
10491             combobox.cls += ' roo-select2-container-multi';
10492         }
10493          var indicator = {
10494             tag : 'i',
10495             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10496             tooltip : 'This field is required'
10497         };
10498         if (Roo.bootstrap.version == 4) {
10499             indicator = {
10500                 tag : 'i',
10501                 style : 'display:none'
10502             };
10503         }
10504         
10505         
10506         if (align ==='left' && this.fieldLabel.length) {
10507             
10508             cfg.cls += ' roo-form-group-label-left row';
10509
10510             cfg.cn = [
10511                 indicator,
10512                 {
10513                     tag: 'label',
10514                     'for' :  id,
10515                     cls : 'control-label',
10516                     html : this.fieldLabel
10517
10518                 },
10519                 {
10520                     cls : "", 
10521                     cn: [
10522                         combobox
10523                     ]
10524                 }
10525
10526             ];
10527             
10528             var labelCfg = cfg.cn[1];
10529             var contentCfg = cfg.cn[2];
10530             
10531             if(this.indicatorpos == 'right'){
10532                 cfg.cn = [
10533                     {
10534                         tag: 'label',
10535                         'for' :  id,
10536                         cls : 'control-label',
10537                         cn : [
10538                             {
10539                                 tag : 'span',
10540                                 html : this.fieldLabel
10541                             },
10542                             indicator
10543                         ]
10544                     },
10545                     {
10546                         cls : "", 
10547                         cn: [
10548                             combobox
10549                         ]
10550                     }
10551
10552                 ];
10553                 
10554                 labelCfg = cfg.cn[0];
10555                 contentCfg = cfg.cn[1];
10556             }
10557             
10558             if(this.labelWidth > 12){
10559                 labelCfg.style = "width: " + this.labelWidth + 'px';
10560             }
10561             
10562             if(this.labelWidth < 13 && this.labelmd == 0){
10563                 this.labelmd = this.labelWidth;
10564             }
10565             
10566             if(this.labellg > 0){
10567                 labelCfg.cls += ' col-lg-' + this.labellg;
10568                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10569             }
10570             
10571             if(this.labelmd > 0){
10572                 labelCfg.cls += ' col-md-' + this.labelmd;
10573                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10574             }
10575             
10576             if(this.labelsm > 0){
10577                 labelCfg.cls += ' col-sm-' + this.labelsm;
10578                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10579             }
10580             
10581             if(this.labelxs > 0){
10582                 labelCfg.cls += ' col-xs-' + this.labelxs;
10583                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10584             }
10585             
10586         } else if ( this.fieldLabel.length) {
10587 //                Roo.log(" label");
10588             cfg.cn = [
10589                 indicator,
10590                {
10591                    tag: 'label',
10592                    //cls : 'input-group-addon',
10593                    html : this.fieldLabel
10594
10595                },
10596
10597                combobox
10598
10599             ];
10600             
10601             if(this.indicatorpos == 'right'){
10602                 
10603                 cfg.cn = [
10604                     {
10605                        tag: 'label',
10606                        cn : [
10607                            {
10608                                tag : 'span',
10609                                html : this.fieldLabel
10610                            },
10611                            indicator
10612                        ]
10613
10614                     },
10615                     combobox
10616
10617                 ];
10618
10619             }
10620
10621         } else {
10622             
10623 //                Roo.log(" no label && no align");
10624                 cfg = combobox
10625                      
10626                 
10627         }
10628         
10629         var settings=this;
10630         ['xs','sm','md','lg'].map(function(size){
10631             if (settings[size]) {
10632                 cfg.cls += ' col-' + size + '-' + settings[size];
10633             }
10634         });
10635         
10636         return cfg;
10637         
10638     },
10639     
10640     
10641     
10642     // private
10643     onResize : function(w, h){
10644 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10645 //        if(typeof w == 'number'){
10646 //            var x = w - this.trigger.getWidth();
10647 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10648 //            this.trigger.setStyle('left', x+'px');
10649 //        }
10650     },
10651
10652     // private
10653     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10654
10655     // private
10656     getResizeEl : function(){
10657         return this.inputEl();
10658     },
10659
10660     // private
10661     getPositionEl : function(){
10662         return this.inputEl();
10663     },
10664
10665     // private
10666     alignErrorIcon : function(){
10667         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10668     },
10669
10670     // private
10671     initEvents : function(){
10672         
10673         this.createList();
10674         
10675         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10676         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10677         if(!this.multiple && this.showToggleBtn){
10678             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10679             if(this.hideTrigger){
10680                 this.trigger.setDisplayed(false);
10681             }
10682             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10683         }
10684         
10685         if(this.multiple){
10686             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10687         }
10688         
10689         if(this.removable && !this.editable && !this.tickable){
10690             var close = this.closeTriggerEl();
10691             
10692             if(close){
10693                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10694                 close.on('click', this.removeBtnClick, this, close);
10695             }
10696         }
10697         
10698         //this.trigger.addClassOnOver('x-form-trigger-over');
10699         //this.trigger.addClassOnClick('x-form-trigger-click');
10700         
10701         //if(!this.width){
10702         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10703         //}
10704     },
10705     
10706     closeTriggerEl : function()
10707     {
10708         var close = this.el.select('.roo-combo-removable-btn', true).first();
10709         return close ? close : false;
10710     },
10711     
10712     removeBtnClick : function(e, h, el)
10713     {
10714         e.preventDefault();
10715         
10716         if(this.fireEvent("remove", this) !== false){
10717             this.reset();
10718             this.fireEvent("afterremove", this)
10719         }
10720     },
10721     
10722     createList : function()
10723     {
10724         this.list = Roo.get(document.body).createChild({
10725             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10726             cls: 'typeahead typeahead-long dropdown-menu',
10727             style: 'display:none'
10728         });
10729         
10730         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10731         
10732     },
10733
10734     // private
10735     initTrigger : function(){
10736        
10737     },
10738
10739     // private
10740     onDestroy : function(){
10741         if(this.trigger){
10742             this.trigger.removeAllListeners();
10743           //  this.trigger.remove();
10744         }
10745         //if(this.wrap){
10746         //    this.wrap.remove();
10747         //}
10748         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10749     },
10750
10751     // private
10752     onFocus : function(){
10753         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10754         /*
10755         if(!this.mimicing){
10756             this.wrap.addClass('x-trigger-wrap-focus');
10757             this.mimicing = true;
10758             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10759             if(this.monitorTab){
10760                 this.el.on("keydown", this.checkTab, this);
10761             }
10762         }
10763         */
10764     },
10765
10766     // private
10767     checkTab : function(e){
10768         if(e.getKey() == e.TAB){
10769             this.triggerBlur();
10770         }
10771     },
10772
10773     // private
10774     onBlur : function(){
10775         // do nothing
10776     },
10777
10778     // private
10779     mimicBlur : function(e, t){
10780         /*
10781         if(!this.wrap.contains(t) && this.validateBlur()){
10782             this.triggerBlur();
10783         }
10784         */
10785     },
10786
10787     // private
10788     triggerBlur : function(){
10789         this.mimicing = false;
10790         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10791         if(this.monitorTab){
10792             this.el.un("keydown", this.checkTab, this);
10793         }
10794         //this.wrap.removeClass('x-trigger-wrap-focus');
10795         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10796     },
10797
10798     // private
10799     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10800     validateBlur : function(e, t){
10801         return true;
10802     },
10803
10804     // private
10805     onDisable : function(){
10806         this.inputEl().dom.disabled = true;
10807         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10808         //if(this.wrap){
10809         //    this.wrap.addClass('x-item-disabled');
10810         //}
10811     },
10812
10813     // private
10814     onEnable : function(){
10815         this.inputEl().dom.disabled = false;
10816         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10817         //if(this.wrap){
10818         //    this.el.removeClass('x-item-disabled');
10819         //}
10820     },
10821
10822     // private
10823     onShow : function(){
10824         var ae = this.getActionEl();
10825         
10826         if(ae){
10827             ae.dom.style.display = '';
10828             ae.dom.style.visibility = 'visible';
10829         }
10830     },
10831
10832     // private
10833     
10834     onHide : function(){
10835         var ae = this.getActionEl();
10836         ae.dom.style.display = 'none';
10837     },
10838
10839     /**
10840      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10841      * by an implementing function.
10842      * @method
10843      * @param {EventObject} e
10844      */
10845     onTriggerClick : Roo.emptyFn
10846 });
10847  /*
10848  * Based on:
10849  * Ext JS Library 1.1.1
10850  * Copyright(c) 2006-2007, Ext JS, LLC.
10851  *
10852  * Originally Released Under LGPL - original licence link has changed is not relivant.
10853  *
10854  * Fork - LGPL
10855  * <script type="text/javascript">
10856  */
10857
10858
10859 /**
10860  * @class Roo.data.SortTypes
10861  * @singleton
10862  * Defines the default sorting (casting?) comparison functions used when sorting data.
10863  */
10864 Roo.data.SortTypes = {
10865     /**
10866      * Default sort that does nothing
10867      * @param {Mixed} s The value being converted
10868      * @return {Mixed} The comparison value
10869      */
10870     none : function(s){
10871         return s;
10872     },
10873     
10874     /**
10875      * The regular expression used to strip tags
10876      * @type {RegExp}
10877      * @property
10878      */
10879     stripTagsRE : /<\/?[^>]+>/gi,
10880     
10881     /**
10882      * Strips all HTML tags to sort on text only
10883      * @param {Mixed} s The value being converted
10884      * @return {String} The comparison value
10885      */
10886     asText : function(s){
10887         return String(s).replace(this.stripTagsRE, "");
10888     },
10889     
10890     /**
10891      * Strips all HTML tags to sort on text only - Case insensitive
10892      * @param {Mixed} s The value being converted
10893      * @return {String} The comparison value
10894      */
10895     asUCText : function(s){
10896         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10897     },
10898     
10899     /**
10900      * Case insensitive string
10901      * @param {Mixed} s The value being converted
10902      * @return {String} The comparison value
10903      */
10904     asUCString : function(s) {
10905         return String(s).toUpperCase();
10906     },
10907     
10908     /**
10909      * Date sorting
10910      * @param {Mixed} s The value being converted
10911      * @return {Number} The comparison value
10912      */
10913     asDate : function(s) {
10914         if(!s){
10915             return 0;
10916         }
10917         if(s instanceof Date){
10918             return s.getTime();
10919         }
10920         return Date.parse(String(s));
10921     },
10922     
10923     /**
10924      * Float sorting
10925      * @param {Mixed} s The value being converted
10926      * @return {Float} The comparison value
10927      */
10928     asFloat : function(s) {
10929         var val = parseFloat(String(s).replace(/,/g, ""));
10930         if(isNaN(val)) {
10931             val = 0;
10932         }
10933         return val;
10934     },
10935     
10936     /**
10937      * Integer sorting
10938      * @param {Mixed} s The value being converted
10939      * @return {Number} The comparison value
10940      */
10941     asInt : function(s) {
10942         var val = parseInt(String(s).replace(/,/g, ""));
10943         if(isNaN(val)) {
10944             val = 0;
10945         }
10946         return val;
10947     }
10948 };/*
10949  * Based on:
10950  * Ext JS Library 1.1.1
10951  * Copyright(c) 2006-2007, Ext JS, LLC.
10952  *
10953  * Originally Released Under LGPL - original licence link has changed is not relivant.
10954  *
10955  * Fork - LGPL
10956  * <script type="text/javascript">
10957  */
10958
10959 /**
10960 * @class Roo.data.Record
10961  * Instances of this class encapsulate both record <em>definition</em> information, and record
10962  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10963  * to access Records cached in an {@link Roo.data.Store} object.<br>
10964  * <p>
10965  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10966  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10967  * objects.<br>
10968  * <p>
10969  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10970  * @constructor
10971  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10972  * {@link #create}. The parameters are the same.
10973  * @param {Array} data An associative Array of data values keyed by the field name.
10974  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10975  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10976  * not specified an integer id is generated.
10977  */
10978 Roo.data.Record = function(data, id){
10979     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10980     this.data = data;
10981 };
10982
10983 /**
10984  * Generate a constructor for a specific record layout.
10985  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10986  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10987  * Each field definition object may contain the following properties: <ul>
10988  * <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,
10989  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10990  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10991  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10992  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10993  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10994  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10995  * this may be omitted.</p></li>
10996  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10997  * <ul><li>auto (Default, implies no conversion)</li>
10998  * <li>string</li>
10999  * <li>int</li>
11000  * <li>float</li>
11001  * <li>boolean</li>
11002  * <li>date</li></ul></p></li>
11003  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11004  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11005  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11006  * by the Reader into an object that will be stored in the Record. It is passed the
11007  * following parameters:<ul>
11008  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11009  * </ul></p></li>
11010  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11011  * </ul>
11012  * <br>usage:<br><pre><code>
11013 var TopicRecord = Roo.data.Record.create(
11014     {name: 'title', mapping: 'topic_title'},
11015     {name: 'author', mapping: 'username'},
11016     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11017     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11018     {name: 'lastPoster', mapping: 'user2'},
11019     {name: 'excerpt', mapping: 'post_text'}
11020 );
11021
11022 var myNewRecord = new TopicRecord({
11023     title: 'Do my job please',
11024     author: 'noobie',
11025     totalPosts: 1,
11026     lastPost: new Date(),
11027     lastPoster: 'Animal',
11028     excerpt: 'No way dude!'
11029 });
11030 myStore.add(myNewRecord);
11031 </code></pre>
11032  * @method create
11033  * @static
11034  */
11035 Roo.data.Record.create = function(o){
11036     var f = function(){
11037         f.superclass.constructor.apply(this, arguments);
11038     };
11039     Roo.extend(f, Roo.data.Record);
11040     var p = f.prototype;
11041     p.fields = new Roo.util.MixedCollection(false, function(field){
11042         return field.name;
11043     });
11044     for(var i = 0, len = o.length; i < len; i++){
11045         p.fields.add(new Roo.data.Field(o[i]));
11046     }
11047     f.getField = function(name){
11048         return p.fields.get(name);  
11049     };
11050     return f;
11051 };
11052
11053 Roo.data.Record.AUTO_ID = 1000;
11054 Roo.data.Record.EDIT = 'edit';
11055 Roo.data.Record.REJECT = 'reject';
11056 Roo.data.Record.COMMIT = 'commit';
11057
11058 Roo.data.Record.prototype = {
11059     /**
11060      * Readonly flag - true if this record has been modified.
11061      * @type Boolean
11062      */
11063     dirty : false,
11064     editing : false,
11065     error: null,
11066     modified: null,
11067
11068     // private
11069     join : function(store){
11070         this.store = store;
11071     },
11072
11073     /**
11074      * Set the named field to the specified value.
11075      * @param {String} name The name of the field to set.
11076      * @param {Object} value The value to set the field to.
11077      */
11078     set : function(name, value){
11079         if(this.data[name] == value){
11080             return;
11081         }
11082         this.dirty = true;
11083         if(!this.modified){
11084             this.modified = {};
11085         }
11086         if(typeof this.modified[name] == 'undefined'){
11087             this.modified[name] = this.data[name];
11088         }
11089         this.data[name] = value;
11090         if(!this.editing && this.store){
11091             this.store.afterEdit(this);
11092         }       
11093     },
11094
11095     /**
11096      * Get the value of the named field.
11097      * @param {String} name The name of the field to get the value of.
11098      * @return {Object} The value of the field.
11099      */
11100     get : function(name){
11101         return this.data[name]; 
11102     },
11103
11104     // private
11105     beginEdit : function(){
11106         this.editing = true;
11107         this.modified = {}; 
11108     },
11109
11110     // private
11111     cancelEdit : function(){
11112         this.editing = false;
11113         delete this.modified;
11114     },
11115
11116     // private
11117     endEdit : function(){
11118         this.editing = false;
11119         if(this.dirty && this.store){
11120             this.store.afterEdit(this);
11121         }
11122     },
11123
11124     /**
11125      * Usually called by the {@link Roo.data.Store} which owns the Record.
11126      * Rejects all changes made to the Record since either creation, or the last commit operation.
11127      * Modified fields are reverted to their original values.
11128      * <p>
11129      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11130      * of reject operations.
11131      */
11132     reject : function(){
11133         var m = this.modified;
11134         for(var n in m){
11135             if(typeof m[n] != "function"){
11136                 this.data[n] = m[n];
11137             }
11138         }
11139         this.dirty = false;
11140         delete this.modified;
11141         this.editing = false;
11142         if(this.store){
11143             this.store.afterReject(this);
11144         }
11145     },
11146
11147     /**
11148      * Usually called by the {@link Roo.data.Store} which owns the Record.
11149      * Commits all changes made to the Record since either creation, or the last commit operation.
11150      * <p>
11151      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11152      * of commit operations.
11153      */
11154     commit : function(){
11155         this.dirty = false;
11156         delete this.modified;
11157         this.editing = false;
11158         if(this.store){
11159             this.store.afterCommit(this);
11160         }
11161     },
11162
11163     // private
11164     hasError : function(){
11165         return this.error != null;
11166     },
11167
11168     // private
11169     clearError : function(){
11170         this.error = null;
11171     },
11172
11173     /**
11174      * Creates a copy of this record.
11175      * @param {String} id (optional) A new record id if you don't want to use this record's id
11176      * @return {Record}
11177      */
11178     copy : function(newId) {
11179         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11180     }
11181 };/*
11182  * Based on:
11183  * Ext JS Library 1.1.1
11184  * Copyright(c) 2006-2007, Ext JS, LLC.
11185  *
11186  * Originally Released Under LGPL - original licence link has changed is not relivant.
11187  *
11188  * Fork - LGPL
11189  * <script type="text/javascript">
11190  */
11191
11192
11193
11194 /**
11195  * @class Roo.data.Store
11196  * @extends Roo.util.Observable
11197  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11198  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11199  * <p>
11200  * 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
11201  * has no knowledge of the format of the data returned by the Proxy.<br>
11202  * <p>
11203  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11204  * instances from the data object. These records are cached and made available through accessor functions.
11205  * @constructor
11206  * Creates a new Store.
11207  * @param {Object} config A config object containing the objects needed for the Store to access data,
11208  * and read the data into Records.
11209  */
11210 Roo.data.Store = function(config){
11211     this.data = new Roo.util.MixedCollection(false);
11212     this.data.getKey = function(o){
11213         return o.id;
11214     };
11215     this.baseParams = {};
11216     // private
11217     this.paramNames = {
11218         "start" : "start",
11219         "limit" : "limit",
11220         "sort" : "sort",
11221         "dir" : "dir",
11222         "multisort" : "_multisort"
11223     };
11224
11225     if(config && config.data){
11226         this.inlineData = config.data;
11227         delete config.data;
11228     }
11229
11230     Roo.apply(this, config);
11231     
11232     if(this.reader){ // reader passed
11233         this.reader = Roo.factory(this.reader, Roo.data);
11234         this.reader.xmodule = this.xmodule || false;
11235         if(!this.recordType){
11236             this.recordType = this.reader.recordType;
11237         }
11238         if(this.reader.onMetaChange){
11239             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11240         }
11241     }
11242
11243     if(this.recordType){
11244         this.fields = this.recordType.prototype.fields;
11245     }
11246     this.modified = [];
11247
11248     this.addEvents({
11249         /**
11250          * @event datachanged
11251          * Fires when the data cache has changed, and a widget which is using this Store
11252          * as a Record cache should refresh its view.
11253          * @param {Store} this
11254          */
11255         datachanged : true,
11256         /**
11257          * @event metachange
11258          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11259          * @param {Store} this
11260          * @param {Object} meta The JSON metadata
11261          */
11262         metachange : true,
11263         /**
11264          * @event add
11265          * Fires when Records have been added to the Store
11266          * @param {Store} this
11267          * @param {Roo.data.Record[]} records The array of Records added
11268          * @param {Number} index The index at which the record(s) were added
11269          */
11270         add : true,
11271         /**
11272          * @event remove
11273          * Fires when a Record has been removed from the Store
11274          * @param {Store} this
11275          * @param {Roo.data.Record} record The Record that was removed
11276          * @param {Number} index The index at which the record was removed
11277          */
11278         remove : true,
11279         /**
11280          * @event update
11281          * Fires when a Record has been updated
11282          * @param {Store} this
11283          * @param {Roo.data.Record} record The Record that was updated
11284          * @param {String} operation The update operation being performed.  Value may be one of:
11285          * <pre><code>
11286  Roo.data.Record.EDIT
11287  Roo.data.Record.REJECT
11288  Roo.data.Record.COMMIT
11289          * </code></pre>
11290          */
11291         update : true,
11292         /**
11293          * @event clear
11294          * Fires when the data cache has been cleared.
11295          * @param {Store} this
11296          */
11297         clear : true,
11298         /**
11299          * @event beforeload
11300          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11301          * the load action will be canceled.
11302          * @param {Store} this
11303          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11304          */
11305         beforeload : true,
11306         /**
11307          * @event beforeloadadd
11308          * Fires after a new set of Records has been loaded.
11309          * @param {Store} this
11310          * @param {Roo.data.Record[]} records The Records that were loaded
11311          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11312          */
11313         beforeloadadd : true,
11314         /**
11315          * @event load
11316          * Fires after a new set of Records has been loaded, before they are added to the store.
11317          * @param {Store} this
11318          * @param {Roo.data.Record[]} records The Records that were loaded
11319          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11320          * @params {Object} return from reader
11321          */
11322         load : true,
11323         /**
11324          * @event loadexception
11325          * Fires if an exception occurs in the Proxy during loading.
11326          * Called with the signature of the Proxy's "loadexception" event.
11327          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11328          * 
11329          * @param {Proxy} 
11330          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11331          * @param {Object} load options 
11332          * @param {Object} jsonData from your request (normally this contains the Exception)
11333          */
11334         loadexception : true
11335     });
11336     
11337     if(this.proxy){
11338         this.proxy = Roo.factory(this.proxy, Roo.data);
11339         this.proxy.xmodule = this.xmodule || false;
11340         this.relayEvents(this.proxy,  ["loadexception"]);
11341     }
11342     this.sortToggle = {};
11343     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11344
11345     Roo.data.Store.superclass.constructor.call(this);
11346
11347     if(this.inlineData){
11348         this.loadData(this.inlineData);
11349         delete this.inlineData;
11350     }
11351 };
11352
11353 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11354      /**
11355     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11356     * without a remote query - used by combo/forms at present.
11357     */
11358     
11359     /**
11360     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11361     */
11362     /**
11363     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11364     */
11365     /**
11366     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11367     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11368     */
11369     /**
11370     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11371     * on any HTTP request
11372     */
11373     /**
11374     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11375     */
11376     /**
11377     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11378     */
11379     multiSort: false,
11380     /**
11381     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11382     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11383     */
11384     remoteSort : false,
11385
11386     /**
11387     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11388      * loaded or when a record is removed. (defaults to false).
11389     */
11390     pruneModifiedRecords : false,
11391
11392     // private
11393     lastOptions : null,
11394
11395     /**
11396      * Add Records to the Store and fires the add event.
11397      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11398      */
11399     add : function(records){
11400         records = [].concat(records);
11401         for(var i = 0, len = records.length; i < len; i++){
11402             records[i].join(this);
11403         }
11404         var index = this.data.length;
11405         this.data.addAll(records);
11406         this.fireEvent("add", this, records, index);
11407     },
11408
11409     /**
11410      * Remove a Record from the Store and fires the remove event.
11411      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11412      */
11413     remove : function(record){
11414         var index = this.data.indexOf(record);
11415         this.data.removeAt(index);
11416  
11417         if(this.pruneModifiedRecords){
11418             this.modified.remove(record);
11419         }
11420         this.fireEvent("remove", this, record, index);
11421     },
11422
11423     /**
11424      * Remove all Records from the Store and fires the clear event.
11425      */
11426     removeAll : function(){
11427         this.data.clear();
11428         if(this.pruneModifiedRecords){
11429             this.modified = [];
11430         }
11431         this.fireEvent("clear", this);
11432     },
11433
11434     /**
11435      * Inserts Records to the Store at the given index and fires the add event.
11436      * @param {Number} index The start index at which to insert the passed Records.
11437      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11438      */
11439     insert : function(index, records){
11440         records = [].concat(records);
11441         for(var i = 0, len = records.length; i < len; i++){
11442             this.data.insert(index, records[i]);
11443             records[i].join(this);
11444         }
11445         this.fireEvent("add", this, records, index);
11446     },
11447
11448     /**
11449      * Get the index within the cache of the passed Record.
11450      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11451      * @return {Number} The index of the passed Record. Returns -1 if not found.
11452      */
11453     indexOf : function(record){
11454         return this.data.indexOf(record);
11455     },
11456
11457     /**
11458      * Get the index within the cache of the Record with the passed id.
11459      * @param {String} id The id of the Record to find.
11460      * @return {Number} The index of the Record. Returns -1 if not found.
11461      */
11462     indexOfId : function(id){
11463         return this.data.indexOfKey(id);
11464     },
11465
11466     /**
11467      * Get the Record with the specified id.
11468      * @param {String} id The id of the Record to find.
11469      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11470      */
11471     getById : function(id){
11472         return this.data.key(id);
11473     },
11474
11475     /**
11476      * Get the Record at the specified index.
11477      * @param {Number} index The index of the Record to find.
11478      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11479      */
11480     getAt : function(index){
11481         return this.data.itemAt(index);
11482     },
11483
11484     /**
11485      * Returns a range of Records between specified indices.
11486      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11487      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11488      * @return {Roo.data.Record[]} An array of Records
11489      */
11490     getRange : function(start, end){
11491         return this.data.getRange(start, end);
11492     },
11493
11494     // private
11495     storeOptions : function(o){
11496         o = Roo.apply({}, o);
11497         delete o.callback;
11498         delete o.scope;
11499         this.lastOptions = o;
11500     },
11501
11502     /**
11503      * Loads the Record cache from the configured Proxy using the configured Reader.
11504      * <p>
11505      * If using remote paging, then the first load call must specify the <em>start</em>
11506      * and <em>limit</em> properties in the options.params property to establish the initial
11507      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11508      * <p>
11509      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11510      * and this call will return before the new data has been loaded. Perform any post-processing
11511      * in a callback function, or in a "load" event handler.</strong>
11512      * <p>
11513      * @param {Object} options An object containing properties which control loading options:<ul>
11514      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11515      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11516      * passed the following arguments:<ul>
11517      * <li>r : Roo.data.Record[]</li>
11518      * <li>options: Options object from the load call</li>
11519      * <li>success: Boolean success indicator</li></ul></li>
11520      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11521      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11522      * </ul>
11523      */
11524     load : function(options){
11525         options = options || {};
11526         if(this.fireEvent("beforeload", this, options) !== false){
11527             this.storeOptions(options);
11528             var p = Roo.apply(options.params || {}, this.baseParams);
11529             // if meta was not loaded from remote source.. try requesting it.
11530             if (!this.reader.metaFromRemote) {
11531                 p._requestMeta = 1;
11532             }
11533             if(this.sortInfo && this.remoteSort){
11534                 var pn = this.paramNames;
11535                 p[pn["sort"]] = this.sortInfo.field;
11536                 p[pn["dir"]] = this.sortInfo.direction;
11537             }
11538             if (this.multiSort) {
11539                 var pn = this.paramNames;
11540                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11541             }
11542             
11543             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11544         }
11545     },
11546
11547     /**
11548      * Reloads the Record cache from the configured Proxy using the configured Reader and
11549      * the options from the last load operation performed.
11550      * @param {Object} options (optional) An object containing properties which may override the options
11551      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11552      * the most recently used options are reused).
11553      */
11554     reload : function(options){
11555         this.load(Roo.applyIf(options||{}, this.lastOptions));
11556     },
11557
11558     // private
11559     // Called as a callback by the Reader during a load operation.
11560     loadRecords : function(o, options, success){
11561         if(!o || success === false){
11562             if(success !== false){
11563                 this.fireEvent("load", this, [], options, o);
11564             }
11565             if(options.callback){
11566                 options.callback.call(options.scope || this, [], options, false);
11567             }
11568             return;
11569         }
11570         // if data returned failure - throw an exception.
11571         if (o.success === false) {
11572             // show a message if no listener is registered.
11573             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11574                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11575             }
11576             // loadmask wil be hooked into this..
11577             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11578             return;
11579         }
11580         var r = o.records, t = o.totalRecords || r.length;
11581         
11582         this.fireEvent("beforeloadadd", this, r, options, o);
11583         
11584         if(!options || options.add !== true){
11585             if(this.pruneModifiedRecords){
11586                 this.modified = [];
11587             }
11588             for(var i = 0, len = r.length; i < len; i++){
11589                 r[i].join(this);
11590             }
11591             if(this.snapshot){
11592                 this.data = this.snapshot;
11593                 delete this.snapshot;
11594             }
11595             this.data.clear();
11596             this.data.addAll(r);
11597             this.totalLength = t;
11598             this.applySort();
11599             this.fireEvent("datachanged", this);
11600         }else{
11601             this.totalLength = Math.max(t, this.data.length+r.length);
11602             this.add(r);
11603         }
11604         
11605         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11606                 
11607             var e = new Roo.data.Record({});
11608
11609             e.set(this.parent.displayField, this.parent.emptyTitle);
11610             e.set(this.parent.valueField, '');
11611
11612             this.insert(0, e);
11613         }
11614             
11615         this.fireEvent("load", this, r, options, o);
11616         if(options.callback){
11617             options.callback.call(options.scope || this, r, options, true);
11618         }
11619     },
11620
11621
11622     /**
11623      * Loads data from a passed data block. A Reader which understands the format of the data
11624      * must have been configured in the constructor.
11625      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11626      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11627      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11628      */
11629     loadData : function(o, append){
11630         var r = this.reader.readRecords(o);
11631         this.loadRecords(r, {add: append}, true);
11632     },
11633
11634     /**
11635      * Gets the number of cached records.
11636      * <p>
11637      * <em>If using paging, this may not be the total size of the dataset. If the data object
11638      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11639      * the data set size</em>
11640      */
11641     getCount : function(){
11642         return this.data.length || 0;
11643     },
11644
11645     /**
11646      * Gets the total number of records in the dataset as returned by the server.
11647      * <p>
11648      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11649      * the dataset size</em>
11650      */
11651     getTotalCount : function(){
11652         return this.totalLength || 0;
11653     },
11654
11655     /**
11656      * Returns the sort state of the Store as an object with two properties:
11657      * <pre><code>
11658  field {String} The name of the field by which the Records are sorted
11659  direction {String} The sort order, "ASC" or "DESC"
11660      * </code></pre>
11661      */
11662     getSortState : function(){
11663         return this.sortInfo;
11664     },
11665
11666     // private
11667     applySort : function(){
11668         if(this.sortInfo && !this.remoteSort){
11669             var s = this.sortInfo, f = s.field;
11670             var st = this.fields.get(f).sortType;
11671             var fn = function(r1, r2){
11672                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11673                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11674             };
11675             this.data.sort(s.direction, fn);
11676             if(this.snapshot && this.snapshot != this.data){
11677                 this.snapshot.sort(s.direction, fn);
11678             }
11679         }
11680     },
11681
11682     /**
11683      * Sets the default sort column and order to be used by the next load operation.
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     setDefaultSort : function(field, dir){
11688         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11689     },
11690
11691     /**
11692      * Sort the Records.
11693      * If remote sorting is used, the sort is performed on the server, and the cache is
11694      * reloaded. If local sorting is used, the cache is sorted internally.
11695      * @param {String} fieldName The name of the field to sort by.
11696      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11697      */
11698     sort : function(fieldName, dir){
11699         var f = this.fields.get(fieldName);
11700         if(!dir){
11701             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11702             
11703             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11704                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11705             }else{
11706                 dir = f.sortDir;
11707             }
11708         }
11709         this.sortToggle[f.name] = dir;
11710         this.sortInfo = {field: f.name, direction: dir};
11711         if(!this.remoteSort){
11712             this.applySort();
11713             this.fireEvent("datachanged", this);
11714         }else{
11715             this.load(this.lastOptions);
11716         }
11717     },
11718
11719     /**
11720      * Calls the specified function for each of the Records in the cache.
11721      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11722      * Returning <em>false</em> aborts and exits the iteration.
11723      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11724      */
11725     each : function(fn, scope){
11726         this.data.each(fn, scope);
11727     },
11728
11729     /**
11730      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11731      * (e.g., during paging).
11732      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11733      */
11734     getModifiedRecords : function(){
11735         return this.modified;
11736     },
11737
11738     // private
11739     createFilterFn : function(property, value, anyMatch){
11740         if(!value.exec){ // not a regex
11741             value = String(value);
11742             if(value.length == 0){
11743                 return false;
11744             }
11745             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11746         }
11747         return function(r){
11748             return value.test(r.data[property]);
11749         };
11750     },
11751
11752     /**
11753      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11754      * @param {String} property A field on your records
11755      * @param {Number} start The record index to start at (defaults to 0)
11756      * @param {Number} end The last record index to include (defaults to length - 1)
11757      * @return {Number} The sum
11758      */
11759     sum : function(property, start, end){
11760         var rs = this.data.items, v = 0;
11761         start = start || 0;
11762         end = (end || end === 0) ? end : rs.length-1;
11763
11764         for(var i = start; i <= end; i++){
11765             v += (rs[i].data[property] || 0);
11766         }
11767         return v;
11768     },
11769
11770     /**
11771      * Filter the records by a specified property.
11772      * @param {String} field A field on your records
11773      * @param {String/RegExp} value Either a string that the field
11774      * should start with or a RegExp to test against the field
11775      * @param {Boolean} anyMatch True to match any part not just the beginning
11776      */
11777     filter : function(property, value, anyMatch){
11778         var fn = this.createFilterFn(property, value, anyMatch);
11779         return fn ? this.filterBy(fn) : this.clearFilter();
11780     },
11781
11782     /**
11783      * Filter by a function. The specified function will be called with each
11784      * record in this data source. If the function returns true the record is included,
11785      * otherwise it is filtered.
11786      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11787      * @param {Object} scope (optional) The scope of the function (defaults to this)
11788      */
11789     filterBy : function(fn, scope){
11790         this.snapshot = this.snapshot || this.data;
11791         this.data = this.queryBy(fn, scope||this);
11792         this.fireEvent("datachanged", this);
11793     },
11794
11795     /**
11796      * Query the records by a specified property.
11797      * @param {String} field A field on your records
11798      * @param {String/RegExp} value Either a string that the field
11799      * should start with or a RegExp to test against the field
11800      * @param {Boolean} anyMatch True to match any part not just the beginning
11801      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11802      */
11803     query : function(property, value, anyMatch){
11804         var fn = this.createFilterFn(property, value, anyMatch);
11805         return fn ? this.queryBy(fn) : this.data.clone();
11806     },
11807
11808     /**
11809      * Query by a function. The specified function will be called with each
11810      * record in this data source. If the function returns true the record is included
11811      * in the results.
11812      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11813      * @param {Object} scope (optional) The scope of the function (defaults to this)
11814       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11815      **/
11816     queryBy : function(fn, scope){
11817         var data = this.snapshot || this.data;
11818         return data.filterBy(fn, scope||this);
11819     },
11820
11821     /**
11822      * Collects unique values for a particular dataIndex from this store.
11823      * @param {String} dataIndex The property to collect
11824      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11825      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11826      * @return {Array} An array of the unique values
11827      **/
11828     collect : function(dataIndex, allowNull, bypassFilter){
11829         var d = (bypassFilter === true && this.snapshot) ?
11830                 this.snapshot.items : this.data.items;
11831         var v, sv, r = [], l = {};
11832         for(var i = 0, len = d.length; i < len; i++){
11833             v = d[i].data[dataIndex];
11834             sv = String(v);
11835             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11836                 l[sv] = true;
11837                 r[r.length] = v;
11838             }
11839         }
11840         return r;
11841     },
11842
11843     /**
11844      * Revert to a view of the Record cache with no filtering applied.
11845      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11846      */
11847     clearFilter : function(suppressEvent){
11848         if(this.snapshot && this.snapshot != this.data){
11849             this.data = this.snapshot;
11850             delete this.snapshot;
11851             if(suppressEvent !== true){
11852                 this.fireEvent("datachanged", this);
11853             }
11854         }
11855     },
11856
11857     // private
11858     afterEdit : function(record){
11859         if(this.modified.indexOf(record) == -1){
11860             this.modified.push(record);
11861         }
11862         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11863     },
11864     
11865     // private
11866     afterReject : function(record){
11867         this.modified.remove(record);
11868         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11869     },
11870
11871     // private
11872     afterCommit : function(record){
11873         this.modified.remove(record);
11874         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11875     },
11876
11877     /**
11878      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11879      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11880      */
11881     commitChanges : 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].commit();
11886         }
11887     },
11888
11889     /**
11890      * Cancel outstanding changes on all changed records.
11891      */
11892     rejectChanges : function(){
11893         var m = this.modified.slice(0);
11894         this.modified = [];
11895         for(var i = 0, len = m.length; i < len; i++){
11896             m[i].reject();
11897         }
11898     },
11899
11900     onMetaChange : function(meta, rtype, o){
11901         this.recordType = rtype;
11902         this.fields = rtype.prototype.fields;
11903         delete this.snapshot;
11904         this.sortInfo = meta.sortInfo || this.sortInfo;
11905         this.modified = [];
11906         this.fireEvent('metachange', this, this.reader.meta);
11907     },
11908     
11909     moveIndex : function(data, type)
11910     {
11911         var index = this.indexOf(data);
11912         
11913         var newIndex = index + type;
11914         
11915         this.remove(data);
11916         
11917         this.insert(newIndex, data);
11918         
11919     }
11920 });/*
11921  * Based on:
11922  * Ext JS Library 1.1.1
11923  * Copyright(c) 2006-2007, Ext JS, LLC.
11924  *
11925  * Originally Released Under LGPL - original licence link has changed is not relivant.
11926  *
11927  * Fork - LGPL
11928  * <script type="text/javascript">
11929  */
11930
11931 /**
11932  * @class Roo.data.SimpleStore
11933  * @extends Roo.data.Store
11934  * Small helper class to make creating Stores from Array data easier.
11935  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11936  * @cfg {Array} fields An array of field definition objects, or field name strings.
11937  * @cfg {Array} data The multi-dimensional array of data
11938  * @constructor
11939  * @param {Object} config
11940  */
11941 Roo.data.SimpleStore = function(config){
11942     Roo.data.SimpleStore.superclass.constructor.call(this, {
11943         isLocal : true,
11944         reader: new Roo.data.ArrayReader({
11945                 id: config.id
11946             },
11947             Roo.data.Record.create(config.fields)
11948         ),
11949         proxy : new Roo.data.MemoryProxy(config.data)
11950     });
11951     this.load();
11952 };
11953 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11954  * Based on:
11955  * Ext JS Library 1.1.1
11956  * Copyright(c) 2006-2007, Ext JS, LLC.
11957  *
11958  * Originally Released Under LGPL - original licence link has changed is not relivant.
11959  *
11960  * Fork - LGPL
11961  * <script type="text/javascript">
11962  */
11963
11964 /**
11965 /**
11966  * @extends Roo.data.Store
11967  * @class Roo.data.JsonStore
11968  * Small helper class to make creating Stores for JSON data easier. <br/>
11969 <pre><code>
11970 var store = new Roo.data.JsonStore({
11971     url: 'get-images.php',
11972     root: 'images',
11973     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11974 });
11975 </code></pre>
11976  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11977  * JsonReader and HttpProxy (unless inline data is provided).</b>
11978  * @cfg {Array} fields An array of field definition objects, or field name strings.
11979  * @constructor
11980  * @param {Object} config
11981  */
11982 Roo.data.JsonStore = function(c){
11983     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11984         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11985         reader: new Roo.data.JsonReader(c, c.fields)
11986     }));
11987 };
11988 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11989  * Based on:
11990  * Ext JS Library 1.1.1
11991  * Copyright(c) 2006-2007, Ext JS, LLC.
11992  *
11993  * Originally Released Under LGPL - original licence link has changed is not relivant.
11994  *
11995  * Fork - LGPL
11996  * <script type="text/javascript">
11997  */
11998
11999  
12000 Roo.data.Field = function(config){
12001     if(typeof config == "string"){
12002         config = {name: config};
12003     }
12004     Roo.apply(this, config);
12005     
12006     if(!this.type){
12007         this.type = "auto";
12008     }
12009     
12010     var st = Roo.data.SortTypes;
12011     // named sortTypes are supported, here we look them up
12012     if(typeof this.sortType == "string"){
12013         this.sortType = st[this.sortType];
12014     }
12015     
12016     // set default sortType for strings and dates
12017     if(!this.sortType){
12018         switch(this.type){
12019             case "string":
12020                 this.sortType = st.asUCString;
12021                 break;
12022             case "date":
12023                 this.sortType = st.asDate;
12024                 break;
12025             default:
12026                 this.sortType = st.none;
12027         }
12028     }
12029
12030     // define once
12031     var stripRe = /[\$,%]/g;
12032
12033     // prebuilt conversion function for this field, instead of
12034     // switching every time we're reading a value
12035     if(!this.convert){
12036         var cv, dateFormat = this.dateFormat;
12037         switch(this.type){
12038             case "":
12039             case "auto":
12040             case undefined:
12041                 cv = function(v){ return v; };
12042                 break;
12043             case "string":
12044                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12045                 break;
12046             case "int":
12047                 cv = function(v){
12048                     return v !== undefined && v !== null && v !== '' ?
12049                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12050                     };
12051                 break;
12052             case "float":
12053                 cv = function(v){
12054                     return v !== undefined && v !== null && v !== '' ?
12055                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12056                     };
12057                 break;
12058             case "bool":
12059             case "boolean":
12060                 cv = function(v){ return v === true || v === "true" || v == 1; };
12061                 break;
12062             case "date":
12063                 cv = function(v){
12064                     if(!v){
12065                         return '';
12066                     }
12067                     if(v instanceof Date){
12068                         return v;
12069                     }
12070                     if(dateFormat){
12071                         if(dateFormat == "timestamp"){
12072                             return new Date(v*1000);
12073                         }
12074                         return Date.parseDate(v, dateFormat);
12075                     }
12076                     var parsed = Date.parse(v);
12077                     return parsed ? new Date(parsed) : null;
12078                 };
12079              break;
12080             
12081         }
12082         this.convert = cv;
12083     }
12084 };
12085
12086 Roo.data.Field.prototype = {
12087     dateFormat: null,
12088     defaultValue: "",
12089     mapping: null,
12090     sortType : null,
12091     sortDir : "ASC"
12092 };/*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102  
12103 // Base class for reading structured data from a data source.  This class is intended to be
12104 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12105
12106 /**
12107  * @class Roo.data.DataReader
12108  * Base class for reading structured data from a data source.  This class is intended to be
12109  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12110  */
12111
12112 Roo.data.DataReader = function(meta, recordType){
12113     
12114     this.meta = meta;
12115     
12116     this.recordType = recordType instanceof Array ? 
12117         Roo.data.Record.create(recordType) : recordType;
12118 };
12119
12120 Roo.data.DataReader.prototype = {
12121      /**
12122      * Create an empty record
12123      * @param {Object} data (optional) - overlay some values
12124      * @return {Roo.data.Record} record created.
12125      */
12126     newRow :  function(d) {
12127         var da =  {};
12128         this.recordType.prototype.fields.each(function(c) {
12129             switch( c.type) {
12130                 case 'int' : da[c.name] = 0; break;
12131                 case 'date' : da[c.name] = new Date(); break;
12132                 case 'float' : da[c.name] = 0.0; break;
12133                 case 'boolean' : da[c.name] = false; break;
12134                 default : da[c.name] = ""; break;
12135             }
12136             
12137         });
12138         return new this.recordType(Roo.apply(da, d));
12139     }
12140     
12141 };/*
12142  * Based on:
12143  * Ext JS Library 1.1.1
12144  * Copyright(c) 2006-2007, Ext JS, LLC.
12145  *
12146  * Originally Released Under LGPL - original licence link has changed is not relivant.
12147  *
12148  * Fork - LGPL
12149  * <script type="text/javascript">
12150  */
12151
12152 /**
12153  * @class Roo.data.DataProxy
12154  * @extends Roo.data.Observable
12155  * This class is an abstract base class for implementations which provide retrieval of
12156  * unformatted data objects.<br>
12157  * <p>
12158  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12159  * (of the appropriate type which knows how to parse the data object) to provide a block of
12160  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12161  * <p>
12162  * Custom implementations must implement the load method as described in
12163  * {@link Roo.data.HttpProxy#load}.
12164  */
12165 Roo.data.DataProxy = function(){
12166     this.addEvents({
12167         /**
12168          * @event beforeload
12169          * Fires before a network request is made to retrieve a data object.
12170          * @param {Object} This DataProxy object.
12171          * @param {Object} params The params parameter to the load function.
12172          */
12173         beforeload : true,
12174         /**
12175          * @event load
12176          * Fires before the load method's callback is called.
12177          * @param {Object} This DataProxy object.
12178          * @param {Object} o The data object.
12179          * @param {Object} arg The callback argument object passed to the load function.
12180          */
12181         load : true,
12182         /**
12183          * @event loadexception
12184          * Fires if an Exception occurs during data retrieval.
12185          * @param {Object} This DataProxy object.
12186          * @param {Object} o The data object.
12187          * @param {Object} arg The callback argument object passed to the load function.
12188          * @param {Object} e The Exception.
12189          */
12190         loadexception : true
12191     });
12192     Roo.data.DataProxy.superclass.constructor.call(this);
12193 };
12194
12195 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12196
12197     /**
12198      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12199      */
12200 /*
12201  * Based on:
12202  * Ext JS Library 1.1.1
12203  * Copyright(c) 2006-2007, Ext JS, LLC.
12204  *
12205  * Originally Released Under LGPL - original licence link has changed is not relivant.
12206  *
12207  * Fork - LGPL
12208  * <script type="text/javascript">
12209  */
12210 /**
12211  * @class Roo.data.MemoryProxy
12212  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12213  * to the Reader when its load method is called.
12214  * @constructor
12215  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12216  */
12217 Roo.data.MemoryProxy = function(data){
12218     if (data.data) {
12219         data = data.data;
12220     }
12221     Roo.data.MemoryProxy.superclass.constructor.call(this);
12222     this.data = data;
12223 };
12224
12225 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12226     
12227     /**
12228      * Load data from the requested source (in this case an in-memory
12229      * data object passed to the constructor), read the data object into
12230      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12231      * process that block using the passed callback.
12232      * @param {Object} params This parameter is not used by the MemoryProxy class.
12233      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12234      * object into a block of Roo.data.Records.
12235      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12236      * The function must be passed <ul>
12237      * <li>The Record block object</li>
12238      * <li>The "arg" argument from the load function</li>
12239      * <li>A boolean success indicator</li>
12240      * </ul>
12241      * @param {Object} scope The scope in which to call the callback
12242      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12243      */
12244     load : function(params, reader, callback, scope, arg){
12245         params = params || {};
12246         var result;
12247         try {
12248             result = reader.readRecords(this.data);
12249         }catch(e){
12250             this.fireEvent("loadexception", this, arg, null, e);
12251             callback.call(scope, null, arg, false);
12252             return;
12253         }
12254         callback.call(scope, result, arg, true);
12255     },
12256     
12257     // private
12258     update : function(params, records){
12259         
12260     }
12261 });/*
12262  * Based on:
12263  * Ext JS Library 1.1.1
12264  * Copyright(c) 2006-2007, Ext JS, LLC.
12265  *
12266  * Originally Released Under LGPL - original licence link has changed is not relivant.
12267  *
12268  * Fork - LGPL
12269  * <script type="text/javascript">
12270  */
12271 /**
12272  * @class Roo.data.HttpProxy
12273  * @extends Roo.data.DataProxy
12274  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12275  * configured to reference a certain URL.<br><br>
12276  * <p>
12277  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12278  * from which the running page was served.<br><br>
12279  * <p>
12280  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12281  * <p>
12282  * Be aware that to enable the browser to parse an XML document, the server must set
12283  * the Content-Type header in the HTTP response to "text/xml".
12284  * @constructor
12285  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12286  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12287  * will be used to make the request.
12288  */
12289 Roo.data.HttpProxy = function(conn){
12290     Roo.data.HttpProxy.superclass.constructor.call(this);
12291     // is conn a conn config or a real conn?
12292     this.conn = conn;
12293     this.useAjax = !conn || !conn.events;
12294   
12295 };
12296
12297 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12298     // thse are take from connection...
12299     
12300     /**
12301      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12302      */
12303     /**
12304      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12305      * extra parameters to each request made by this object. (defaults to undefined)
12306      */
12307     /**
12308      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12309      *  to each request made by this object. (defaults to undefined)
12310      */
12311     /**
12312      * @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)
12313      */
12314     /**
12315      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12316      */
12317      /**
12318      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12319      * @type Boolean
12320      */
12321   
12322
12323     /**
12324      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12325      * @type Boolean
12326      */
12327     /**
12328      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12329      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12330      * a finer-grained basis than the DataProxy events.
12331      */
12332     getConnection : function(){
12333         return this.useAjax ? Roo.Ajax : this.conn;
12334     },
12335
12336     /**
12337      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12338      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12339      * process that block using the passed callback.
12340      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12341      * for the request to the remote server.
12342      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12343      * object into a block of Roo.data.Records.
12344      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12345      * The function must be passed <ul>
12346      * <li>The Record block object</li>
12347      * <li>The "arg" argument from the load function</li>
12348      * <li>A boolean success indicator</li>
12349      * </ul>
12350      * @param {Object} scope The scope in which to call the callback
12351      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12352      */
12353     load : function(params, reader, callback, scope, arg){
12354         if(this.fireEvent("beforeload", this, params) !== false){
12355             var  o = {
12356                 params : params || {},
12357                 request: {
12358                     callback : callback,
12359                     scope : scope,
12360                     arg : arg
12361                 },
12362                 reader: reader,
12363                 callback : this.loadResponse,
12364                 scope: this
12365             };
12366             if(this.useAjax){
12367                 Roo.applyIf(o, this.conn);
12368                 if(this.activeRequest){
12369                     Roo.Ajax.abort(this.activeRequest);
12370                 }
12371                 this.activeRequest = Roo.Ajax.request(o);
12372             }else{
12373                 this.conn.request(o);
12374             }
12375         }else{
12376             callback.call(scope||this, null, arg, false);
12377         }
12378     },
12379
12380     // private
12381     loadResponse : function(o, success, response){
12382         delete this.activeRequest;
12383         if(!success){
12384             this.fireEvent("loadexception", this, o, response);
12385             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12386             return;
12387         }
12388         var result;
12389         try {
12390             result = o.reader.read(response);
12391         }catch(e){
12392             this.fireEvent("loadexception", this, o, response, e);
12393             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12394             return;
12395         }
12396         
12397         this.fireEvent("load", this, o, o.request.arg);
12398         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12399     },
12400
12401     // private
12402     update : function(dataSet){
12403
12404     },
12405
12406     // private
12407     updateResponse : function(dataSet){
12408
12409     }
12410 });/*
12411  * Based on:
12412  * Ext JS Library 1.1.1
12413  * Copyright(c) 2006-2007, Ext JS, LLC.
12414  *
12415  * Originally Released Under LGPL - original licence link has changed is not relivant.
12416  *
12417  * Fork - LGPL
12418  * <script type="text/javascript">
12419  */
12420
12421 /**
12422  * @class Roo.data.ScriptTagProxy
12423  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12424  * other than the originating domain of the running page.<br><br>
12425  * <p>
12426  * <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
12427  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12428  * <p>
12429  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12430  * source code that is used as the source inside a &lt;script> tag.<br><br>
12431  * <p>
12432  * In order for the browser to process the returned data, the server must wrap the data object
12433  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12434  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12435  * depending on whether the callback name was passed:
12436  * <p>
12437  * <pre><code>
12438 boolean scriptTag = false;
12439 String cb = request.getParameter("callback");
12440 if (cb != null) {
12441     scriptTag = true;
12442     response.setContentType("text/javascript");
12443 } else {
12444     response.setContentType("application/x-json");
12445 }
12446 Writer out = response.getWriter();
12447 if (scriptTag) {
12448     out.write(cb + "(");
12449 }
12450 out.print(dataBlock.toJsonString());
12451 if (scriptTag) {
12452     out.write(");");
12453 }
12454 </pre></code>
12455  *
12456  * @constructor
12457  * @param {Object} config A configuration object.
12458  */
12459 Roo.data.ScriptTagProxy = function(config){
12460     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12461     Roo.apply(this, config);
12462     this.head = document.getElementsByTagName("head")[0];
12463 };
12464
12465 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12466
12467 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12468     /**
12469      * @cfg {String} url The URL from which to request the data object.
12470      */
12471     /**
12472      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12473      */
12474     timeout : 30000,
12475     /**
12476      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12477      * the server the name of the callback function set up by the load call to process the returned data object.
12478      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12479      * javascript output which calls this named function passing the data object as its only parameter.
12480      */
12481     callbackParam : "callback",
12482     /**
12483      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12484      * name to the request.
12485      */
12486     nocache : true,
12487
12488     /**
12489      * Load data from the configured URL, read the data object into
12490      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12491      * process that block using the passed callback.
12492      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12493      * for the request to the remote server.
12494      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12495      * object into a block of Roo.data.Records.
12496      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12497      * The function must be passed <ul>
12498      * <li>The Record block object</li>
12499      * <li>The "arg" argument from the load function</li>
12500      * <li>A boolean success indicator</li>
12501      * </ul>
12502      * @param {Object} scope The scope in which to call the callback
12503      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12504      */
12505     load : function(params, reader, callback, scope, arg){
12506         if(this.fireEvent("beforeload", this, params) !== false){
12507
12508             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12509
12510             var url = this.url;
12511             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12512             if(this.nocache){
12513                 url += "&_dc=" + (new Date().getTime());
12514             }
12515             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12516             var trans = {
12517                 id : transId,
12518                 cb : "stcCallback"+transId,
12519                 scriptId : "stcScript"+transId,
12520                 params : params,
12521                 arg : arg,
12522                 url : url,
12523                 callback : callback,
12524                 scope : scope,
12525                 reader : reader
12526             };
12527             var conn = this;
12528
12529             window[trans.cb] = function(o){
12530                 conn.handleResponse(o, trans);
12531             };
12532
12533             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12534
12535             if(this.autoAbort !== false){
12536                 this.abort();
12537             }
12538
12539             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12540
12541             var script = document.createElement("script");
12542             script.setAttribute("src", url);
12543             script.setAttribute("type", "text/javascript");
12544             script.setAttribute("id", trans.scriptId);
12545             this.head.appendChild(script);
12546
12547             this.trans = trans;
12548         }else{
12549             callback.call(scope||this, null, arg, false);
12550         }
12551     },
12552
12553     // private
12554     isLoading : function(){
12555         return this.trans ? true : false;
12556     },
12557
12558     /**
12559      * Abort the current server request.
12560      */
12561     abort : function(){
12562         if(this.isLoading()){
12563             this.destroyTrans(this.trans);
12564         }
12565     },
12566
12567     // private
12568     destroyTrans : function(trans, isLoaded){
12569         this.head.removeChild(document.getElementById(trans.scriptId));
12570         clearTimeout(trans.timeoutId);
12571         if(isLoaded){
12572             window[trans.cb] = undefined;
12573             try{
12574                 delete window[trans.cb];
12575             }catch(e){}
12576         }else{
12577             // if hasn't been loaded, wait for load to remove it to prevent script error
12578             window[trans.cb] = function(){
12579                 window[trans.cb] = undefined;
12580                 try{
12581                     delete window[trans.cb];
12582                 }catch(e){}
12583             };
12584         }
12585     },
12586
12587     // private
12588     handleResponse : function(o, trans){
12589         this.trans = false;
12590         this.destroyTrans(trans, true);
12591         var result;
12592         try {
12593             result = trans.reader.readRecords(o);
12594         }catch(e){
12595             this.fireEvent("loadexception", this, o, trans.arg, e);
12596             trans.callback.call(trans.scope||window, null, trans.arg, false);
12597             return;
12598         }
12599         this.fireEvent("load", this, o, trans.arg);
12600         trans.callback.call(trans.scope||window, result, trans.arg, true);
12601     },
12602
12603     // private
12604     handleFailure : function(trans){
12605         this.trans = false;
12606         this.destroyTrans(trans, false);
12607         this.fireEvent("loadexception", this, null, trans.arg);
12608         trans.callback.call(trans.scope||window, null, trans.arg, false);
12609     }
12610 });/*
12611  * Based on:
12612  * Ext JS Library 1.1.1
12613  * Copyright(c) 2006-2007, Ext JS, LLC.
12614  *
12615  * Originally Released Under LGPL - original licence link has changed is not relivant.
12616  *
12617  * Fork - LGPL
12618  * <script type="text/javascript">
12619  */
12620
12621 /**
12622  * @class Roo.data.JsonReader
12623  * @extends Roo.data.DataReader
12624  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12625  * based on mappings in a provided Roo.data.Record constructor.
12626  * 
12627  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12628  * in the reply previously. 
12629  * 
12630  * <p>
12631  * Example code:
12632  * <pre><code>
12633 var RecordDef = Roo.data.Record.create([
12634     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12635     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12636 ]);
12637 var myReader = new Roo.data.JsonReader({
12638     totalProperty: "results",    // The property which contains the total dataset size (optional)
12639     root: "rows",                // The property which contains an Array of row objects
12640     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12641 }, RecordDef);
12642 </code></pre>
12643  * <p>
12644  * This would consume a JSON file like this:
12645  * <pre><code>
12646 { 'results': 2, 'rows': [
12647     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12648     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12649 }
12650 </code></pre>
12651  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12652  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12653  * paged from the remote server.
12654  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12655  * @cfg {String} root name of the property which contains the Array of row objects.
12656  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12657  * @cfg {Array} fields Array of field definition objects
12658  * @constructor
12659  * Create a new JsonReader
12660  * @param {Object} meta Metadata configuration options
12661  * @param {Object} recordType Either an Array of field definition objects,
12662  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12663  */
12664 Roo.data.JsonReader = function(meta, recordType){
12665     
12666     meta = meta || {};
12667     // set some defaults:
12668     Roo.applyIf(meta, {
12669         totalProperty: 'total',
12670         successProperty : 'success',
12671         root : 'data',
12672         id : 'id'
12673     });
12674     
12675     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12676 };
12677 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12678     
12679     /**
12680      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12681      * Used by Store query builder to append _requestMeta to params.
12682      * 
12683      */
12684     metaFromRemote : false,
12685     /**
12686      * This method is only used by a DataProxy which has retrieved data from a remote server.
12687      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12688      * @return {Object} data A data block which is used by an Roo.data.Store object as
12689      * a cache of Roo.data.Records.
12690      */
12691     read : function(response){
12692         var json = response.responseText;
12693        
12694         var o = /* eval:var:o */ eval("("+json+")");
12695         if(!o) {
12696             throw {message: "JsonReader.read: Json object not found"};
12697         }
12698         
12699         if(o.metaData){
12700             
12701             delete this.ef;
12702             this.metaFromRemote = true;
12703             this.meta = o.metaData;
12704             this.recordType = Roo.data.Record.create(o.metaData.fields);
12705             this.onMetaChange(this.meta, this.recordType, o);
12706         }
12707         return this.readRecords(o);
12708     },
12709
12710     // private function a store will implement
12711     onMetaChange : function(meta, recordType, o){
12712
12713     },
12714
12715     /**
12716          * @ignore
12717          */
12718     simpleAccess: function(obj, subsc) {
12719         return obj[subsc];
12720     },
12721
12722         /**
12723          * @ignore
12724          */
12725     getJsonAccessor: function(){
12726         var re = /[\[\.]/;
12727         return function(expr) {
12728             try {
12729                 return(re.test(expr))
12730                     ? new Function("obj", "return obj." + expr)
12731                     : function(obj){
12732                         return obj[expr];
12733                     };
12734             } catch(e){}
12735             return Roo.emptyFn;
12736         };
12737     }(),
12738
12739     /**
12740      * Create a data block containing Roo.data.Records from an XML document.
12741      * @param {Object} o An object which contains an Array of row objects in the property specified
12742      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12743      * which contains the total size of the dataset.
12744      * @return {Object} data A data block which is used by an Roo.data.Store object as
12745      * a cache of Roo.data.Records.
12746      */
12747     readRecords : function(o){
12748         /**
12749          * After any data loads, the raw JSON data is available for further custom processing.
12750          * @type Object
12751          */
12752         this.o = o;
12753         var s = this.meta, Record = this.recordType,
12754             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12755
12756 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12757         if (!this.ef) {
12758             if(s.totalProperty) {
12759                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12760                 }
12761                 if(s.successProperty) {
12762                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12763                 }
12764                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12765                 if (s.id) {
12766                         var g = this.getJsonAccessor(s.id);
12767                         this.getId = function(rec) {
12768                                 var r = g(rec);  
12769                                 return (r === undefined || r === "") ? null : r;
12770                         };
12771                 } else {
12772                         this.getId = function(){return null;};
12773                 }
12774             this.ef = [];
12775             for(var jj = 0; jj < fl; jj++){
12776                 f = fi[jj];
12777                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12778                 this.ef[jj] = this.getJsonAccessor(map);
12779             }
12780         }
12781
12782         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12783         if(s.totalProperty){
12784             var vt = parseInt(this.getTotal(o), 10);
12785             if(!isNaN(vt)){
12786                 totalRecords = vt;
12787             }
12788         }
12789         if(s.successProperty){
12790             var vs = this.getSuccess(o);
12791             if(vs === false || vs === 'false'){
12792                 success = false;
12793             }
12794         }
12795         var records = [];
12796         for(var i = 0; i < c; i++){
12797                 var n = root[i];
12798             var values = {};
12799             var id = this.getId(n);
12800             for(var j = 0; j < fl; j++){
12801                 f = fi[j];
12802             var v = this.ef[j](n);
12803             if (!f.convert) {
12804                 Roo.log('missing convert for ' + f.name);
12805                 Roo.log(f);
12806                 continue;
12807             }
12808             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12809             }
12810             var record = new Record(values, id);
12811             record.json = n;
12812             records[i] = record;
12813         }
12814         return {
12815             raw : o,
12816             success : success,
12817             records : records,
12818             totalRecords : totalRecords
12819         };
12820     }
12821 });/*
12822  * Based on:
12823  * Ext JS Library 1.1.1
12824  * Copyright(c) 2006-2007, Ext JS, LLC.
12825  *
12826  * Originally Released Under LGPL - original licence link has changed is not relivant.
12827  *
12828  * Fork - LGPL
12829  * <script type="text/javascript">
12830  */
12831
12832 /**
12833  * @class Roo.data.ArrayReader
12834  * @extends Roo.data.DataReader
12835  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12836  * Each element of that Array represents a row of data fields. The
12837  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12838  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12839  * <p>
12840  * Example code:.
12841  * <pre><code>
12842 var RecordDef = Roo.data.Record.create([
12843     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12844     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12845 ]);
12846 var myReader = new Roo.data.ArrayReader({
12847     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12848 }, RecordDef);
12849 </code></pre>
12850  * <p>
12851  * This would consume an Array like this:
12852  * <pre><code>
12853 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12854   </code></pre>
12855  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12856  * @constructor
12857  * Create a new JsonReader
12858  * @param {Object} meta Metadata configuration options.
12859  * @param {Object} recordType Either an Array of field definition objects
12860  * as specified to {@link Roo.data.Record#create},
12861  * or an {@link Roo.data.Record} object
12862  * created using {@link Roo.data.Record#create}.
12863  */
12864 Roo.data.ArrayReader = function(meta, recordType){
12865     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12866 };
12867
12868 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12869     /**
12870      * Create a data block containing Roo.data.Records from an XML document.
12871      * @param {Object} o An Array of row objects which represents the dataset.
12872      * @return {Object} data A data block which is used by an Roo.data.Store object as
12873      * a cache of Roo.data.Records.
12874      */
12875     readRecords : function(o){
12876         var sid = this.meta ? this.meta.id : null;
12877         var recordType = this.recordType, fields = recordType.prototype.fields;
12878         var records = [];
12879         var root = o;
12880             for(var i = 0; i < root.length; i++){
12881                     var n = root[i];
12882                 var values = {};
12883                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12884                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12885                 var f = fields.items[j];
12886                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12887                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12888                 v = f.convert(v);
12889                 values[f.name] = v;
12890             }
12891                 var record = new recordType(values, id);
12892                 record.json = n;
12893                 records[records.length] = record;
12894             }
12895             return {
12896                 records : records,
12897                 totalRecords : records.length
12898             };
12899     }
12900 });/*
12901  * - LGPL
12902  * * 
12903  */
12904
12905 /**
12906  * @class Roo.bootstrap.ComboBox
12907  * @extends Roo.bootstrap.TriggerField
12908  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12909  * @cfg {Boolean} append (true|false) default false
12910  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12911  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12912  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12913  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12914  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12915  * @cfg {Boolean} animate default true
12916  * @cfg {Boolean} emptyResultText only for touch device
12917  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12918  * @cfg {String} emptyTitle default ''
12919  * @constructor
12920  * Create a new ComboBox.
12921  * @param {Object} config Configuration options
12922  */
12923 Roo.bootstrap.ComboBox = function(config){
12924     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12925     this.addEvents({
12926         /**
12927          * @event expand
12928          * Fires when the dropdown list is expanded
12929         * @param {Roo.bootstrap.ComboBox} combo This combo box
12930         */
12931         'expand' : true,
12932         /**
12933          * @event collapse
12934          * Fires when the dropdown list is collapsed
12935         * @param {Roo.bootstrap.ComboBox} combo This combo box
12936         */
12937         'collapse' : true,
12938         /**
12939          * @event beforeselect
12940          * Fires before a list item is selected. Return false to cancel the selection.
12941         * @param {Roo.bootstrap.ComboBox} combo This combo box
12942         * @param {Roo.data.Record} record The data record returned from the underlying store
12943         * @param {Number} index The index of the selected item in the dropdown list
12944         */
12945         'beforeselect' : true,
12946         /**
12947          * @event select
12948          * Fires when a list item is selected
12949         * @param {Roo.bootstrap.ComboBox} combo This combo box
12950         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12951         * @param {Number} index The index of the selected item in the dropdown list
12952         */
12953         'select' : true,
12954         /**
12955          * @event beforequery
12956          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12957          * The event object passed has these properties:
12958         * @param {Roo.bootstrap.ComboBox} combo This combo box
12959         * @param {String} query The query
12960         * @param {Boolean} forceAll true to force "all" query
12961         * @param {Boolean} cancel true to cancel the query
12962         * @param {Object} e The query event object
12963         */
12964         'beforequery': true,
12965          /**
12966          * @event add
12967          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12968         * @param {Roo.bootstrap.ComboBox} combo This combo box
12969         */
12970         'add' : true,
12971         /**
12972          * @event edit
12973          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12974         * @param {Roo.bootstrap.ComboBox} combo This combo box
12975         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12976         */
12977         'edit' : true,
12978         /**
12979          * @event remove
12980          * Fires when the remove value from the combobox array
12981         * @param {Roo.bootstrap.ComboBox} combo This combo box
12982         */
12983         'remove' : true,
12984         /**
12985          * @event afterremove
12986          * Fires when the remove value from the combobox array
12987         * @param {Roo.bootstrap.ComboBox} combo This combo box
12988         */
12989         'afterremove' : true,
12990         /**
12991          * @event specialfilter
12992          * Fires when specialfilter
12993             * @param {Roo.bootstrap.ComboBox} combo This combo box
12994             */
12995         'specialfilter' : true,
12996         /**
12997          * @event tick
12998          * Fires when tick the element
12999             * @param {Roo.bootstrap.ComboBox} combo This combo box
13000             */
13001         'tick' : true,
13002         /**
13003          * @event touchviewdisplay
13004          * Fires when touch view require special display (default is using displayField)
13005             * @param {Roo.bootstrap.ComboBox} combo This combo box
13006             * @param {Object} cfg set html .
13007             */
13008         'touchviewdisplay' : true
13009         
13010     });
13011     
13012     this.item = [];
13013     this.tickItems = [];
13014     
13015     this.selectedIndex = -1;
13016     if(this.mode == 'local'){
13017         if(config.queryDelay === undefined){
13018             this.queryDelay = 10;
13019         }
13020         if(config.minChars === undefined){
13021             this.minChars = 0;
13022         }
13023     }
13024 };
13025
13026 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13027      
13028     /**
13029      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13030      * rendering into an Roo.Editor, defaults to false)
13031      */
13032     /**
13033      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13034      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13035      */
13036     /**
13037      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13038      */
13039     /**
13040      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13041      * the dropdown list (defaults to undefined, with no header element)
13042      */
13043
13044      /**
13045      * @cfg {String/Roo.Template} tpl The template to use to render the output
13046      */
13047      
13048      /**
13049      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13050      */
13051     listWidth: undefined,
13052     /**
13053      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13054      * mode = 'remote' or 'text' if mode = 'local')
13055      */
13056     displayField: undefined,
13057     
13058     /**
13059      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13060      * mode = 'remote' or 'value' if mode = 'local'). 
13061      * Note: use of a valueField requires the user make a selection
13062      * in order for a value to be mapped.
13063      */
13064     valueField: undefined,
13065     /**
13066      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13067      */
13068     modalTitle : '',
13069     
13070     /**
13071      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13072      * field's data value (defaults to the underlying DOM element's name)
13073      */
13074     hiddenName: undefined,
13075     /**
13076      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13077      */
13078     listClass: '',
13079     /**
13080      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13081      */
13082     selectedClass: 'active',
13083     
13084     /**
13085      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13086      */
13087     shadow:'sides',
13088     /**
13089      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13090      * anchor positions (defaults to 'tl-bl')
13091      */
13092     listAlign: 'tl-bl?',
13093     /**
13094      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13095      */
13096     maxHeight: 300,
13097     /**
13098      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13099      * query specified by the allQuery config option (defaults to 'query')
13100      */
13101     triggerAction: 'query',
13102     /**
13103      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13104      * (defaults to 4, does not apply if editable = false)
13105      */
13106     minChars : 4,
13107     /**
13108      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13109      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13110      */
13111     typeAhead: false,
13112     /**
13113      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13114      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13115      */
13116     queryDelay: 500,
13117     /**
13118      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13119      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13120      */
13121     pageSize: 0,
13122     /**
13123      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13124      * when editable = true (defaults to false)
13125      */
13126     selectOnFocus:false,
13127     /**
13128      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13129      */
13130     queryParam: 'query',
13131     /**
13132      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13133      * when mode = 'remote' (defaults to 'Loading...')
13134      */
13135     loadingText: 'Loading...',
13136     /**
13137      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13138      */
13139     resizable: false,
13140     /**
13141      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13142      */
13143     handleHeight : 8,
13144     /**
13145      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13146      * traditional select (defaults to true)
13147      */
13148     editable: true,
13149     /**
13150      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13151      */
13152     allQuery: '',
13153     /**
13154      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13155      */
13156     mode: 'remote',
13157     /**
13158      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13159      * listWidth has a higher value)
13160      */
13161     minListWidth : 70,
13162     /**
13163      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13164      * allow the user to set arbitrary text into the field (defaults to false)
13165      */
13166     forceSelection:false,
13167     /**
13168      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13169      * if typeAhead = true (defaults to 250)
13170      */
13171     typeAheadDelay : 250,
13172     /**
13173      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13174      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13175      */
13176     valueNotFoundText : undefined,
13177     /**
13178      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13179      */
13180     blockFocus : false,
13181     
13182     /**
13183      * @cfg {Boolean} disableClear Disable showing of clear button.
13184      */
13185     disableClear : false,
13186     /**
13187      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13188      */
13189     alwaysQuery : false,
13190     
13191     /**
13192      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13193      */
13194     multiple : false,
13195     
13196     /**
13197      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
13198      */
13199     invalidClass : "has-warning",
13200     
13201     /**
13202      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
13203      */
13204     validClass : "has-success",
13205     
13206     /**
13207      * @cfg {Boolean} specialFilter (true|false) special filter default false
13208      */
13209     specialFilter : false,
13210     
13211     /**
13212      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13213      */
13214     mobileTouchView : true,
13215     
13216     /**
13217      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13218      */
13219     useNativeIOS : false,
13220     
13221     /**
13222      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13223      */
13224     mobile_restrict_height : false,
13225     
13226     ios_options : false,
13227     
13228     //private
13229     addicon : false,
13230     editicon: false,
13231     
13232     page: 0,
13233     hasQuery: false,
13234     append: false,
13235     loadNext: false,
13236     autoFocus : true,
13237     tickable : false,
13238     btnPosition : 'right',
13239     triggerList : true,
13240     showToggleBtn : true,
13241     animate : true,
13242     emptyResultText: 'Empty',
13243     triggerText : 'Select',
13244     emptyTitle : '',
13245     
13246     // element that contains real text value.. (when hidden is used..)
13247     
13248     getAutoCreate : function()
13249     {   
13250         var cfg = false;
13251         //render
13252         /*
13253          * Render classic select for iso
13254          */
13255         
13256         if(Roo.isIOS && this.useNativeIOS){
13257             cfg = this.getAutoCreateNativeIOS();
13258             return cfg;
13259         }
13260         
13261         /*
13262          * Touch Devices
13263          */
13264         
13265         if(Roo.isTouch && this.mobileTouchView){
13266             cfg = this.getAutoCreateTouchView();
13267             return cfg;;
13268         }
13269         
13270         /*
13271          *  Normal ComboBox
13272          */
13273         if(!this.tickable){
13274             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13275             return cfg;
13276         }
13277         
13278         /*
13279          *  ComboBox with tickable selections
13280          */
13281              
13282         var align = this.labelAlign || this.parentLabelAlign();
13283         
13284         cfg = {
13285             cls : 'form-group roo-combobox-tickable' //input-group
13286         };
13287         
13288         var btn_text_select = '';
13289         var btn_text_done = '';
13290         var btn_text_cancel = '';
13291         
13292         if (this.btn_text_show) {
13293             btn_text_select = 'Select';
13294             btn_text_done = 'Done';
13295             btn_text_cancel = 'Cancel'; 
13296         }
13297         
13298         var buttons = {
13299             tag : 'div',
13300             cls : 'tickable-buttons',
13301             cn : [
13302                 {
13303                     tag : 'button',
13304                     type : 'button',
13305                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13306                     //html : this.triggerText
13307                     html: btn_text_select
13308                 },
13309                 {
13310                     tag : 'button',
13311                     type : 'button',
13312                     name : 'ok',
13313                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13314                     //html : 'Done'
13315                     html: btn_text_done
13316                 },
13317                 {
13318                     tag : 'button',
13319                     type : 'button',
13320                     name : 'cancel',
13321                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13322                     //html : 'Cancel'
13323                     html: btn_text_cancel
13324                 }
13325             ]
13326         };
13327         
13328         if(this.editable){
13329             buttons.cn.unshift({
13330                 tag: 'input',
13331                 cls: 'roo-select2-search-field-input'
13332             });
13333         }
13334         
13335         var _this = this;
13336         
13337         Roo.each(buttons.cn, function(c){
13338             if (_this.size) {
13339                 c.cls += ' btn-' + _this.size;
13340             }
13341
13342             if (_this.disabled) {
13343                 c.disabled = true;
13344             }
13345         });
13346         
13347         var box = {
13348             tag: 'div',
13349             cn: [
13350                 {
13351                     tag: 'input',
13352                     type : 'hidden',
13353                     cls: 'form-hidden-field'
13354                 },
13355                 {
13356                     tag: 'ul',
13357                     cls: 'roo-select2-choices',
13358                     cn:[
13359                         {
13360                             tag: 'li',
13361                             cls: 'roo-select2-search-field',
13362                             cn: [
13363                                 buttons
13364                             ]
13365                         }
13366                     ]
13367                 }
13368             ]
13369         };
13370         
13371         var combobox = {
13372             cls: 'roo-select2-container input-group roo-select2-container-multi',
13373             cn: [
13374                 
13375                 box
13376 //                {
13377 //                    tag: 'ul',
13378 //                    cls: 'typeahead typeahead-long dropdown-menu',
13379 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13380 //                }
13381             ]
13382         };
13383         
13384         if(this.hasFeedback && !this.allowBlank){
13385             
13386             var feedback = {
13387                 tag: 'span',
13388                 cls: 'glyphicon form-control-feedback'
13389             };
13390
13391             combobox.cn.push(feedback);
13392         }
13393         
13394         var indicator = {
13395             tag : 'i',
13396             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13397             tooltip : 'This field is required'
13398         };
13399         if (Roo.bootstrap.version == 4) {
13400             indicator = {
13401                 tag : 'i',
13402                 style : 'display:none'
13403             };
13404         }
13405         if (align ==='left' && this.fieldLabel.length) {
13406             
13407             cfg.cls += ' roo-form-group-label-left row';
13408             
13409             cfg.cn = [
13410                 indicator,
13411                 {
13412                     tag: 'label',
13413                     'for' :  id,
13414                     cls : 'control-label col-form-label',
13415                     html : this.fieldLabel
13416
13417                 },
13418                 {
13419                     cls : "", 
13420                     cn: [
13421                         combobox
13422                     ]
13423                 }
13424
13425             ];
13426             
13427             var labelCfg = cfg.cn[1];
13428             var contentCfg = cfg.cn[2];
13429             
13430
13431             if(this.indicatorpos == 'right'){
13432                 
13433                 cfg.cn = [
13434                     {
13435                         tag: 'label',
13436                         'for' :  id,
13437                         cls : 'control-label col-form-label',
13438                         cn : [
13439                             {
13440                                 tag : 'span',
13441                                 html : this.fieldLabel
13442                             },
13443                             indicator
13444                         ]
13445                     },
13446                     {
13447                         cls : "",
13448                         cn: [
13449                             combobox
13450                         ]
13451                     }
13452
13453                 ];
13454                 
13455                 
13456                 
13457                 labelCfg = cfg.cn[0];
13458                 contentCfg = cfg.cn[1];
13459             
13460             }
13461             
13462             if(this.labelWidth > 12){
13463                 labelCfg.style = "width: " + this.labelWidth + 'px';
13464             }
13465             
13466             if(this.labelWidth < 13 && this.labelmd == 0){
13467                 this.labelmd = this.labelWidth;
13468             }
13469             
13470             if(this.labellg > 0){
13471                 labelCfg.cls += ' col-lg-' + this.labellg;
13472                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13473             }
13474             
13475             if(this.labelmd > 0){
13476                 labelCfg.cls += ' col-md-' + this.labelmd;
13477                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13478             }
13479             
13480             if(this.labelsm > 0){
13481                 labelCfg.cls += ' col-sm-' + this.labelsm;
13482                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13483             }
13484             
13485             if(this.labelxs > 0){
13486                 labelCfg.cls += ' col-xs-' + this.labelxs;
13487                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13488             }
13489                 
13490                 
13491         } else if ( this.fieldLabel.length) {
13492 //                Roo.log(" label");
13493                  cfg.cn = [
13494                    indicator,
13495                     {
13496                         tag: 'label',
13497                         //cls : 'input-group-addon',
13498                         html : this.fieldLabel
13499                     },
13500                     combobox
13501                 ];
13502                 
13503                 if(this.indicatorpos == 'right'){
13504                     cfg.cn = [
13505                         {
13506                             tag: 'label',
13507                             //cls : 'input-group-addon',
13508                             html : this.fieldLabel
13509                         },
13510                         indicator,
13511                         combobox
13512                     ];
13513                     
13514                 }
13515
13516         } else {
13517             
13518 //                Roo.log(" no label && no align");
13519                 cfg = combobox
13520                      
13521                 
13522         }
13523          
13524         var settings=this;
13525         ['xs','sm','md','lg'].map(function(size){
13526             if (settings[size]) {
13527                 cfg.cls += ' col-' + size + '-' + settings[size];
13528             }
13529         });
13530         
13531         return cfg;
13532         
13533     },
13534     
13535     _initEventsCalled : false,
13536     
13537     // private
13538     initEvents: function()
13539     {   
13540         if (this._initEventsCalled) { // as we call render... prevent looping...
13541             return;
13542         }
13543         this._initEventsCalled = true;
13544         
13545         if (!this.store) {
13546             throw "can not find store for combo";
13547         }
13548         
13549         this.indicator = this.indicatorEl();
13550         
13551         this.store = Roo.factory(this.store, Roo.data);
13552         this.store.parent = this;
13553         
13554         // if we are building from html. then this element is so complex, that we can not really
13555         // use the rendered HTML.
13556         // so we have to trash and replace the previous code.
13557         if (Roo.XComponent.build_from_html) {
13558             // remove this element....
13559             var e = this.el.dom, k=0;
13560             while (e ) { e = e.previousSibling;  ++k;}
13561
13562             this.el.remove();
13563             
13564             this.el=false;
13565             this.rendered = false;
13566             
13567             this.render(this.parent().getChildContainer(true), k);
13568         }
13569         
13570         if(Roo.isIOS && this.useNativeIOS){
13571             this.initIOSView();
13572             return;
13573         }
13574         
13575         /*
13576          * Touch Devices
13577          */
13578         
13579         if(Roo.isTouch && this.mobileTouchView){
13580             this.initTouchView();
13581             return;
13582         }
13583         
13584         if(this.tickable){
13585             this.initTickableEvents();
13586             return;
13587         }
13588         
13589         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13590         
13591         if(this.hiddenName){
13592             
13593             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13594             
13595             this.hiddenField.dom.value =
13596                 this.hiddenValue !== undefined ? this.hiddenValue :
13597                 this.value !== undefined ? this.value : '';
13598
13599             // prevent input submission
13600             this.el.dom.removeAttribute('name');
13601             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13602              
13603              
13604         }
13605         //if(Roo.isGecko){
13606         //    this.el.dom.setAttribute('autocomplete', 'off');
13607         //}
13608         
13609         var cls = 'x-combo-list';
13610         
13611         //this.list = new Roo.Layer({
13612         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13613         //});
13614         
13615         var _this = this;
13616         
13617         (function(){
13618             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13619             _this.list.setWidth(lw);
13620         }).defer(100);
13621         
13622         this.list.on('mouseover', this.onViewOver, this);
13623         this.list.on('mousemove', this.onViewMove, this);
13624         this.list.on('scroll', this.onViewScroll, this);
13625         
13626         /*
13627         this.list.swallowEvent('mousewheel');
13628         this.assetHeight = 0;
13629
13630         if(this.title){
13631             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13632             this.assetHeight += this.header.getHeight();
13633         }
13634
13635         this.innerList = this.list.createChild({cls:cls+'-inner'});
13636         this.innerList.on('mouseover', this.onViewOver, this);
13637         this.innerList.on('mousemove', this.onViewMove, this);
13638         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13639         
13640         if(this.allowBlank && !this.pageSize && !this.disableClear){
13641             this.footer = this.list.createChild({cls:cls+'-ft'});
13642             this.pageTb = new Roo.Toolbar(this.footer);
13643            
13644         }
13645         if(this.pageSize){
13646             this.footer = this.list.createChild({cls:cls+'-ft'});
13647             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13648                     {pageSize: this.pageSize});
13649             
13650         }
13651         
13652         if (this.pageTb && this.allowBlank && !this.disableClear) {
13653             var _this = this;
13654             this.pageTb.add(new Roo.Toolbar.Fill(), {
13655                 cls: 'x-btn-icon x-btn-clear',
13656                 text: '&#160;',
13657                 handler: function()
13658                 {
13659                     _this.collapse();
13660                     _this.clearValue();
13661                     _this.onSelect(false, -1);
13662                 }
13663             });
13664         }
13665         if (this.footer) {
13666             this.assetHeight += this.footer.getHeight();
13667         }
13668         */
13669             
13670         if(!this.tpl){
13671             this.tpl = Roo.bootstrap.version == 4 ?
13672                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13673                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13674         }
13675
13676         this.view = new Roo.View(this.list, this.tpl, {
13677             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13678         });
13679         //this.view.wrapEl.setDisplayed(false);
13680         this.view.on('click', this.onViewClick, this);
13681         
13682         
13683         this.store.on('beforeload', this.onBeforeLoad, this);
13684         this.store.on('load', this.onLoad, this);
13685         this.store.on('loadexception', this.onLoadException, this);
13686         /*
13687         if(this.resizable){
13688             this.resizer = new Roo.Resizable(this.list,  {
13689                pinned:true, handles:'se'
13690             });
13691             this.resizer.on('resize', function(r, w, h){
13692                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13693                 this.listWidth = w;
13694                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13695                 this.restrictHeight();
13696             }, this);
13697             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13698         }
13699         */
13700         if(!this.editable){
13701             this.editable = true;
13702             this.setEditable(false);
13703         }
13704         
13705         /*
13706         
13707         if (typeof(this.events.add.listeners) != 'undefined') {
13708             
13709             this.addicon = this.wrap.createChild(
13710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13711        
13712             this.addicon.on('click', function(e) {
13713                 this.fireEvent('add', this);
13714             }, this);
13715         }
13716         if (typeof(this.events.edit.listeners) != 'undefined') {
13717             
13718             this.editicon = this.wrap.createChild(
13719                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13720             if (this.addicon) {
13721                 this.editicon.setStyle('margin-left', '40px');
13722             }
13723             this.editicon.on('click', function(e) {
13724                 
13725                 // we fire even  if inothing is selected..
13726                 this.fireEvent('edit', this, this.lastData );
13727                 
13728             }, this);
13729         }
13730         */
13731         
13732         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13733             "up" : function(e){
13734                 this.inKeyMode = true;
13735                 this.selectPrev();
13736             },
13737
13738             "down" : function(e){
13739                 if(!this.isExpanded()){
13740                     this.onTriggerClick();
13741                 }else{
13742                     this.inKeyMode = true;
13743                     this.selectNext();
13744                 }
13745             },
13746
13747             "enter" : function(e){
13748 //                this.onViewClick();
13749                 //return true;
13750                 this.collapse();
13751                 
13752                 if(this.fireEvent("specialkey", this, e)){
13753                     this.onViewClick(false);
13754                 }
13755                 
13756                 return true;
13757             },
13758
13759             "esc" : function(e){
13760                 this.collapse();
13761             },
13762
13763             "tab" : function(e){
13764                 this.collapse();
13765                 
13766                 if(this.fireEvent("specialkey", this, e)){
13767                     this.onViewClick(false);
13768                 }
13769                 
13770                 return true;
13771             },
13772
13773             scope : this,
13774
13775             doRelay : function(foo, bar, hname){
13776                 if(hname == 'down' || this.scope.isExpanded()){
13777                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13778                 }
13779                 return true;
13780             },
13781
13782             forceKeyDown: true
13783         });
13784         
13785         
13786         this.queryDelay = Math.max(this.queryDelay || 10,
13787                 this.mode == 'local' ? 10 : 250);
13788         
13789         
13790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13791         
13792         if(this.typeAhead){
13793             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13794         }
13795         if(this.editable !== false){
13796             this.inputEl().on("keyup", this.onKeyUp, this);
13797         }
13798         if(this.forceSelection){
13799             this.inputEl().on('blur', this.doForce, this);
13800         }
13801         
13802         if(this.multiple){
13803             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13804             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13805         }
13806     },
13807     
13808     initTickableEvents: function()
13809     {   
13810         this.createList();
13811         
13812         if(this.hiddenName){
13813             
13814             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13815             
13816             this.hiddenField.dom.value =
13817                 this.hiddenValue !== undefined ? this.hiddenValue :
13818                 this.value !== undefined ? this.value : '';
13819
13820             // prevent input submission
13821             this.el.dom.removeAttribute('name');
13822             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13823              
13824              
13825         }
13826         
13827 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13828         
13829         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13830         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13831         if(this.triggerList){
13832             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13833         }
13834          
13835         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13836         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13837         
13838         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13839         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13840         
13841         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13842         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13843         
13844         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13845         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13846         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13847         
13848         this.okBtn.hide();
13849         this.cancelBtn.hide();
13850         
13851         var _this = this;
13852         
13853         (function(){
13854             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13855             _this.list.setWidth(lw);
13856         }).defer(100);
13857         
13858         this.list.on('mouseover', this.onViewOver, this);
13859         this.list.on('mousemove', this.onViewMove, this);
13860         
13861         this.list.on('scroll', this.onViewScroll, this);
13862         
13863         if(!this.tpl){
13864             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
13865                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13866         }
13867
13868         this.view = new Roo.View(this.list, this.tpl, {
13869             singleSelect:true,
13870             tickable:true,
13871             parent:this,
13872             store: this.store,
13873             selectedClass: this.selectedClass
13874         });
13875         
13876         //this.view.wrapEl.setDisplayed(false);
13877         this.view.on('click', this.onViewClick, this);
13878         
13879         
13880         
13881         this.store.on('beforeload', this.onBeforeLoad, this);
13882         this.store.on('load', this.onLoad, this);
13883         this.store.on('loadexception', this.onLoadException, this);
13884         
13885         if(this.editable){
13886             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13887                 "up" : function(e){
13888                     this.inKeyMode = true;
13889                     this.selectPrev();
13890                 },
13891
13892                 "down" : function(e){
13893                     this.inKeyMode = true;
13894                     this.selectNext();
13895                 },
13896
13897                 "enter" : function(e){
13898                     if(this.fireEvent("specialkey", this, e)){
13899                         this.onViewClick(false);
13900                     }
13901                     
13902                     return true;
13903                 },
13904
13905                 "esc" : function(e){
13906                     this.onTickableFooterButtonClick(e, false, false);
13907                 },
13908
13909                 "tab" : function(e){
13910                     this.fireEvent("specialkey", this, e);
13911                     
13912                     this.onTickableFooterButtonClick(e, false, false);
13913                     
13914                     return true;
13915                 },
13916
13917                 scope : this,
13918
13919                 doRelay : function(e, fn, key){
13920                     if(this.scope.isExpanded()){
13921                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13922                     }
13923                     return true;
13924                 },
13925
13926                 forceKeyDown: true
13927             });
13928         }
13929         
13930         this.queryDelay = Math.max(this.queryDelay || 10,
13931                 this.mode == 'local' ? 10 : 250);
13932         
13933         
13934         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13935         
13936         if(this.typeAhead){
13937             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13938         }
13939         
13940         if(this.editable !== false){
13941             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13942         }
13943         
13944         this.indicator = this.indicatorEl();
13945         
13946         if(this.indicator){
13947             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13948             this.indicator.hide();
13949         }
13950         
13951     },
13952
13953     onDestroy : function(){
13954         if(this.view){
13955             this.view.setStore(null);
13956             this.view.el.removeAllListeners();
13957             this.view.el.remove();
13958             this.view.purgeListeners();
13959         }
13960         if(this.list){
13961             this.list.dom.innerHTML  = '';
13962         }
13963         
13964         if(this.store){
13965             this.store.un('beforeload', this.onBeforeLoad, this);
13966             this.store.un('load', this.onLoad, this);
13967             this.store.un('loadexception', this.onLoadException, this);
13968         }
13969         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13970     },
13971
13972     // private
13973     fireKey : function(e){
13974         if(e.isNavKeyPress() && !this.list.isVisible()){
13975             this.fireEvent("specialkey", this, e);
13976         }
13977     },
13978
13979     // private
13980     onResize: function(w, h){
13981 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13982 //        
13983 //        if(typeof w != 'number'){
13984 //            // we do not handle it!?!?
13985 //            return;
13986 //        }
13987 //        var tw = this.trigger.getWidth();
13988 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13989 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13990 //        var x = w - tw;
13991 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13992 //            
13993 //        //this.trigger.setStyle('left', x+'px');
13994 //        
13995 //        if(this.list && this.listWidth === undefined){
13996 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13997 //            this.list.setWidth(lw);
13998 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13999 //        }
14000         
14001     
14002         
14003     },
14004
14005     /**
14006      * Allow or prevent the user from directly editing the field text.  If false is passed,
14007      * the user will only be able to select from the items defined in the dropdown list.  This method
14008      * is the runtime equivalent of setting the 'editable' config option at config time.
14009      * @param {Boolean} value True to allow the user to directly edit the field text
14010      */
14011     setEditable : function(value){
14012         if(value == this.editable){
14013             return;
14014         }
14015         this.editable = value;
14016         if(!value){
14017             this.inputEl().dom.setAttribute('readOnly', true);
14018             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14019             this.inputEl().addClass('x-combo-noedit');
14020         }else{
14021             this.inputEl().dom.setAttribute('readOnly', false);
14022             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14023             this.inputEl().removeClass('x-combo-noedit');
14024         }
14025     },
14026
14027     // private
14028     
14029     onBeforeLoad : function(combo,opts){
14030         if(!this.hasFocus){
14031             return;
14032         }
14033          if (!opts.add) {
14034             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14035          }
14036         this.restrictHeight();
14037         this.selectedIndex = -1;
14038     },
14039
14040     // private
14041     onLoad : function(){
14042         
14043         this.hasQuery = false;
14044         
14045         if(!this.hasFocus){
14046             return;
14047         }
14048         
14049         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14050             this.loading.hide();
14051         }
14052         
14053         if(this.store.getCount() > 0){
14054             
14055             this.expand();
14056             this.restrictHeight();
14057             if(this.lastQuery == this.allQuery){
14058                 if(this.editable && !this.tickable){
14059                     this.inputEl().dom.select();
14060                 }
14061                 
14062                 if(
14063                     !this.selectByValue(this.value, true) &&
14064                     this.autoFocus && 
14065                     (
14066                         !this.store.lastOptions ||
14067                         typeof(this.store.lastOptions.add) == 'undefined' || 
14068                         this.store.lastOptions.add != true
14069                     )
14070                 ){
14071                     this.select(0, true);
14072                 }
14073             }else{
14074                 if(this.autoFocus){
14075                     this.selectNext();
14076                 }
14077                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14078                     this.taTask.delay(this.typeAheadDelay);
14079                 }
14080             }
14081         }else{
14082             this.onEmptyResults();
14083         }
14084         
14085         //this.el.focus();
14086     },
14087     // private
14088     onLoadException : function()
14089     {
14090         this.hasQuery = false;
14091         
14092         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14093             this.loading.hide();
14094         }
14095         
14096         if(this.tickable && this.editable){
14097             return;
14098         }
14099         
14100         this.collapse();
14101         // only causes errors at present
14102         //Roo.log(this.store.reader.jsonData);
14103         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14104             // fixme
14105             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14106         //}
14107         
14108         
14109     },
14110     // private
14111     onTypeAhead : function(){
14112         if(this.store.getCount() > 0){
14113             var r = this.store.getAt(0);
14114             var newValue = r.data[this.displayField];
14115             var len = newValue.length;
14116             var selStart = this.getRawValue().length;
14117             
14118             if(selStart != len){
14119                 this.setRawValue(newValue);
14120                 this.selectText(selStart, newValue.length);
14121             }
14122         }
14123     },
14124
14125     // private
14126     onSelect : function(record, index){
14127         
14128         if(this.fireEvent('beforeselect', this, record, index) !== false){
14129         
14130             this.setFromData(index > -1 ? record.data : false);
14131             
14132             this.collapse();
14133             this.fireEvent('select', this, record, index);
14134         }
14135     },
14136
14137     /**
14138      * Returns the currently selected field value or empty string if no value is set.
14139      * @return {String} value The selected value
14140      */
14141     getValue : function()
14142     {
14143         if(Roo.isIOS && this.useNativeIOS){
14144             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14145         }
14146         
14147         if(this.multiple){
14148             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14149         }
14150         
14151         if(this.valueField){
14152             return typeof this.value != 'undefined' ? this.value : '';
14153         }else{
14154             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14155         }
14156     },
14157     
14158     getRawValue : function()
14159     {
14160         if(Roo.isIOS && this.useNativeIOS){
14161             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14162         }
14163         
14164         var v = this.inputEl().getValue();
14165         
14166         return v;
14167     },
14168
14169     /**
14170      * Clears any text/value currently set in the field
14171      */
14172     clearValue : function(){
14173         
14174         if(this.hiddenField){
14175             this.hiddenField.dom.value = '';
14176         }
14177         this.value = '';
14178         this.setRawValue('');
14179         this.lastSelectionText = '';
14180         this.lastData = false;
14181         
14182         var close = this.closeTriggerEl();
14183         
14184         if(close){
14185             close.hide();
14186         }
14187         
14188         this.validate();
14189         
14190     },
14191
14192     /**
14193      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14194      * will be displayed in the field.  If the value does not match the data value of an existing item,
14195      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14196      * Otherwise the field will be blank (although the value will still be set).
14197      * @param {String} value The value to match
14198      */
14199     setValue : function(v)
14200     {
14201         if(Roo.isIOS && this.useNativeIOS){
14202             this.setIOSValue(v);
14203             return;
14204         }
14205         
14206         if(this.multiple){
14207             this.syncValue();
14208             return;
14209         }
14210         
14211         var text = v;
14212         if(this.valueField){
14213             var r = this.findRecord(this.valueField, v);
14214             if(r){
14215                 text = r.data[this.displayField];
14216             }else if(this.valueNotFoundText !== undefined){
14217                 text = this.valueNotFoundText;
14218             }
14219         }
14220         this.lastSelectionText = text;
14221         if(this.hiddenField){
14222             this.hiddenField.dom.value = v;
14223         }
14224         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14225         this.value = v;
14226         
14227         var close = this.closeTriggerEl();
14228         
14229         if(close){
14230             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14231         }
14232         
14233         this.validate();
14234     },
14235     /**
14236      * @property {Object} the last set data for the element
14237      */
14238     
14239     lastData : false,
14240     /**
14241      * Sets the value of the field based on a object which is related to the record format for the store.
14242      * @param {Object} value the value to set as. or false on reset?
14243      */
14244     setFromData : function(o){
14245         
14246         if(this.multiple){
14247             this.addItem(o);
14248             return;
14249         }
14250             
14251         var dv = ''; // display value
14252         var vv = ''; // value value..
14253         this.lastData = o;
14254         if (this.displayField) {
14255             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14256         } else {
14257             // this is an error condition!!!
14258             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14259         }
14260         
14261         if(this.valueField){
14262             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14263         }
14264         
14265         var close = this.closeTriggerEl();
14266         
14267         if(close){
14268             if(dv.length || vv * 1 > 0){
14269                 close.show() ;
14270                 this.blockFocus=true;
14271             } else {
14272                 close.hide();
14273             }             
14274         }
14275         
14276         if(this.hiddenField){
14277             this.hiddenField.dom.value = vv;
14278             
14279             this.lastSelectionText = dv;
14280             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14281             this.value = vv;
14282             return;
14283         }
14284         // no hidden field.. - we store the value in 'value', but still display
14285         // display field!!!!
14286         this.lastSelectionText = dv;
14287         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14288         this.value = vv;
14289         
14290         
14291         
14292     },
14293     // private
14294     reset : function(){
14295         // overridden so that last data is reset..
14296         
14297         if(this.multiple){
14298             this.clearItem();
14299             return;
14300         }
14301         
14302         this.setValue(this.originalValue);
14303         //this.clearInvalid();
14304         this.lastData = false;
14305         if (this.view) {
14306             this.view.clearSelections();
14307         }
14308         
14309         this.validate();
14310     },
14311     // private
14312     findRecord : function(prop, value){
14313         var record;
14314         if(this.store.getCount() > 0){
14315             this.store.each(function(r){
14316                 if(r.data[prop] == value){
14317                     record = r;
14318                     return false;
14319                 }
14320                 return true;
14321             });
14322         }
14323         return record;
14324     },
14325     
14326     getName: function()
14327     {
14328         // returns hidden if it's set..
14329         if (!this.rendered) {return ''};
14330         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14331         
14332     },
14333     // private
14334     onViewMove : function(e, t){
14335         this.inKeyMode = false;
14336     },
14337
14338     // private
14339     onViewOver : function(e, t){
14340         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14341             return;
14342         }
14343         var item = this.view.findItemFromChild(t);
14344         
14345         if(item){
14346             var index = this.view.indexOf(item);
14347             this.select(index, false);
14348         }
14349     },
14350
14351     // private
14352     onViewClick : function(view, doFocus, el, e)
14353     {
14354         var index = this.view.getSelectedIndexes()[0];
14355         
14356         var r = this.store.getAt(index);
14357         
14358         if(this.tickable){
14359             
14360             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14361                 return;
14362             }
14363             
14364             var rm = false;
14365             var _this = this;
14366             
14367             Roo.each(this.tickItems, function(v,k){
14368                 
14369                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14370                     Roo.log(v);
14371                     _this.tickItems.splice(k, 1);
14372                     
14373                     if(typeof(e) == 'undefined' && view == false){
14374                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14375                     }
14376                     
14377                     rm = true;
14378                     return;
14379                 }
14380             });
14381             
14382             if(rm){
14383                 return;
14384             }
14385             
14386             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14387                 this.tickItems.push(r.data);
14388             }
14389             
14390             if(typeof(e) == 'undefined' && view == false){
14391                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14392             }
14393                     
14394             return;
14395         }
14396         
14397         if(r){
14398             this.onSelect(r, index);
14399         }
14400         if(doFocus !== false && !this.blockFocus){
14401             this.inputEl().focus();
14402         }
14403     },
14404
14405     // private
14406     restrictHeight : function(){
14407         //this.innerList.dom.style.height = '';
14408         //var inner = this.innerList.dom;
14409         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14410         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14411         //this.list.beginUpdate();
14412         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14413         this.list.alignTo(this.inputEl(), this.listAlign);
14414         this.list.alignTo(this.inputEl(), this.listAlign);
14415         //this.list.endUpdate();
14416     },
14417
14418     // private
14419     onEmptyResults : function(){
14420         
14421         if(this.tickable && this.editable){
14422             this.hasFocus = false;
14423             this.restrictHeight();
14424             return;
14425         }
14426         
14427         this.collapse();
14428     },
14429
14430     /**
14431      * Returns true if the dropdown list is expanded, else false.
14432      */
14433     isExpanded : function(){
14434         return this.list.isVisible();
14435     },
14436
14437     /**
14438      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14439      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14440      * @param {String} value The data value of the item to select
14441      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14442      * selected item if it is not currently in view (defaults to true)
14443      * @return {Boolean} True if the value matched an item in the list, else false
14444      */
14445     selectByValue : function(v, scrollIntoView){
14446         if(v !== undefined && v !== null){
14447             var r = this.findRecord(this.valueField || this.displayField, v);
14448             if(r){
14449                 this.select(this.store.indexOf(r), scrollIntoView);
14450                 return true;
14451             }
14452         }
14453         return false;
14454     },
14455
14456     /**
14457      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14458      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14459      * @param {Number} index The zero-based index of the list item to select
14460      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14461      * selected item if it is not currently in view (defaults to true)
14462      */
14463     select : function(index, scrollIntoView){
14464         this.selectedIndex = index;
14465         this.view.select(index);
14466         if(scrollIntoView !== false){
14467             var el = this.view.getNode(index);
14468             /*
14469              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14470              */
14471             if(el){
14472                 this.list.scrollChildIntoView(el, false);
14473             }
14474         }
14475     },
14476
14477     // private
14478     selectNext : function(){
14479         var ct = this.store.getCount();
14480         if(ct > 0){
14481             if(this.selectedIndex == -1){
14482                 this.select(0);
14483             }else if(this.selectedIndex < ct-1){
14484                 this.select(this.selectedIndex+1);
14485             }
14486         }
14487     },
14488
14489     // private
14490     selectPrev : function(){
14491         var ct = this.store.getCount();
14492         if(ct > 0){
14493             if(this.selectedIndex == -1){
14494                 this.select(0);
14495             }else if(this.selectedIndex != 0){
14496                 this.select(this.selectedIndex-1);
14497             }
14498         }
14499     },
14500
14501     // private
14502     onKeyUp : function(e){
14503         if(this.editable !== false && !e.isSpecialKey()){
14504             this.lastKey = e.getKey();
14505             this.dqTask.delay(this.queryDelay);
14506         }
14507     },
14508
14509     // private
14510     validateBlur : function(){
14511         return !this.list || !this.list.isVisible();   
14512     },
14513
14514     // private
14515     initQuery : function(){
14516         
14517         var v = this.getRawValue();
14518         
14519         if(this.tickable && this.editable){
14520             v = this.tickableInputEl().getValue();
14521         }
14522         
14523         this.doQuery(v);
14524     },
14525
14526     // private
14527     doForce : function(){
14528         if(this.inputEl().dom.value.length > 0){
14529             this.inputEl().dom.value =
14530                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14531              
14532         }
14533     },
14534
14535     /**
14536      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14537      * query allowing the query action to be canceled if needed.
14538      * @param {String} query The SQL query to execute
14539      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14540      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14541      * saved in the current store (defaults to false)
14542      */
14543     doQuery : function(q, forceAll){
14544         
14545         if(q === undefined || q === null){
14546             q = '';
14547         }
14548         var qe = {
14549             query: q,
14550             forceAll: forceAll,
14551             combo: this,
14552             cancel:false
14553         };
14554         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14555             return false;
14556         }
14557         q = qe.query;
14558         
14559         forceAll = qe.forceAll;
14560         if(forceAll === true || (q.length >= this.minChars)){
14561             
14562             this.hasQuery = true;
14563             
14564             if(this.lastQuery != q || this.alwaysQuery){
14565                 this.lastQuery = q;
14566                 if(this.mode == 'local'){
14567                     this.selectedIndex = -1;
14568                     if(forceAll){
14569                         this.store.clearFilter();
14570                     }else{
14571                         
14572                         if(this.specialFilter){
14573                             this.fireEvent('specialfilter', this);
14574                             this.onLoad();
14575                             return;
14576                         }
14577                         
14578                         this.store.filter(this.displayField, q);
14579                     }
14580                     
14581                     this.store.fireEvent("datachanged", this.store);
14582                     
14583                     this.onLoad();
14584                     
14585                     
14586                 }else{
14587                     
14588                     this.store.baseParams[this.queryParam] = q;
14589                     
14590                     var options = {params : this.getParams(q)};
14591                     
14592                     if(this.loadNext){
14593                         options.add = true;
14594                         options.params.start = this.page * this.pageSize;
14595                     }
14596                     
14597                     this.store.load(options);
14598                     
14599                     /*
14600                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14601                      *  we should expand the list on onLoad
14602                      *  so command out it
14603                      */
14604 //                    this.expand();
14605                 }
14606             }else{
14607                 this.selectedIndex = -1;
14608                 this.onLoad();   
14609             }
14610         }
14611         
14612         this.loadNext = false;
14613     },
14614     
14615     // private
14616     getParams : function(q){
14617         var p = {};
14618         //p[this.queryParam] = q;
14619         
14620         if(this.pageSize){
14621             p.start = 0;
14622             p.limit = this.pageSize;
14623         }
14624         return p;
14625     },
14626
14627     /**
14628      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14629      */
14630     collapse : function(){
14631         if(!this.isExpanded()){
14632             return;
14633         }
14634         
14635         this.list.hide();
14636         
14637         this.hasFocus = false;
14638         
14639         if(this.tickable){
14640             this.okBtn.hide();
14641             this.cancelBtn.hide();
14642             this.trigger.show();
14643             
14644             if(this.editable){
14645                 this.tickableInputEl().dom.value = '';
14646                 this.tickableInputEl().blur();
14647             }
14648             
14649         }
14650         
14651         Roo.get(document).un('mousedown', this.collapseIf, this);
14652         Roo.get(document).un('mousewheel', this.collapseIf, this);
14653         if (!this.editable) {
14654             Roo.get(document).un('keydown', this.listKeyPress, this);
14655         }
14656         this.fireEvent('collapse', this);
14657         
14658         this.validate();
14659     },
14660
14661     // private
14662     collapseIf : function(e){
14663         var in_combo  = e.within(this.el);
14664         var in_list =  e.within(this.list);
14665         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14666         
14667         if (in_combo || in_list || is_list) {
14668             //e.stopPropagation();
14669             return;
14670         }
14671         
14672         if(this.tickable){
14673             this.onTickableFooterButtonClick(e, false, false);
14674         }
14675
14676         this.collapse();
14677         
14678     },
14679
14680     /**
14681      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14682      */
14683     expand : function(){
14684        
14685         if(this.isExpanded() || !this.hasFocus){
14686             return;
14687         }
14688         
14689         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14690         this.list.setWidth(lw);
14691         
14692         Roo.log('expand');
14693         
14694         this.list.show();
14695         
14696         this.restrictHeight();
14697         
14698         if(this.tickable){
14699             
14700             this.tickItems = Roo.apply([], this.item);
14701             
14702             this.okBtn.show();
14703             this.cancelBtn.show();
14704             this.trigger.hide();
14705             
14706             if(this.editable){
14707                 this.tickableInputEl().focus();
14708             }
14709             
14710         }
14711         
14712         Roo.get(document).on('mousedown', this.collapseIf, this);
14713         Roo.get(document).on('mousewheel', this.collapseIf, this);
14714         if (!this.editable) {
14715             Roo.get(document).on('keydown', this.listKeyPress, this);
14716         }
14717         
14718         this.fireEvent('expand', this);
14719     },
14720
14721     // private
14722     // Implements the default empty TriggerField.onTriggerClick function
14723     onTriggerClick : function(e)
14724     {
14725         Roo.log('trigger click');
14726         
14727         if(this.disabled || !this.triggerList){
14728             return;
14729         }
14730         
14731         this.page = 0;
14732         this.loadNext = false;
14733         
14734         if(this.isExpanded()){
14735             this.collapse();
14736             if (!this.blockFocus) {
14737                 this.inputEl().focus();
14738             }
14739             
14740         }else {
14741             this.hasFocus = true;
14742             if(this.triggerAction == 'all') {
14743                 this.doQuery(this.allQuery, true);
14744             } else {
14745                 this.doQuery(this.getRawValue());
14746             }
14747             if (!this.blockFocus) {
14748                 this.inputEl().focus();
14749             }
14750         }
14751     },
14752     
14753     onTickableTriggerClick : function(e)
14754     {
14755         if(this.disabled){
14756             return;
14757         }
14758         
14759         this.page = 0;
14760         this.loadNext = false;
14761         this.hasFocus = true;
14762         
14763         if(this.triggerAction == 'all') {
14764             this.doQuery(this.allQuery, true);
14765         } else {
14766             this.doQuery(this.getRawValue());
14767         }
14768     },
14769     
14770     onSearchFieldClick : function(e)
14771     {
14772         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14773             this.onTickableFooterButtonClick(e, false, false);
14774             return;
14775         }
14776         
14777         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14778             return;
14779         }
14780         
14781         this.page = 0;
14782         this.loadNext = false;
14783         this.hasFocus = true;
14784         
14785         if(this.triggerAction == 'all') {
14786             this.doQuery(this.allQuery, true);
14787         } else {
14788             this.doQuery(this.getRawValue());
14789         }
14790     },
14791     
14792     listKeyPress : function(e)
14793     {
14794         //Roo.log('listkeypress');
14795         // scroll to first matching element based on key pres..
14796         if (e.isSpecialKey()) {
14797             return false;
14798         }
14799         var k = String.fromCharCode(e.getKey()).toUpperCase();
14800         //Roo.log(k);
14801         var match  = false;
14802         var csel = this.view.getSelectedNodes();
14803         var cselitem = false;
14804         if (csel.length) {
14805             var ix = this.view.indexOf(csel[0]);
14806             cselitem  = this.store.getAt(ix);
14807             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14808                 cselitem = false;
14809             }
14810             
14811         }
14812         
14813         this.store.each(function(v) { 
14814             if (cselitem) {
14815                 // start at existing selection.
14816                 if (cselitem.id == v.id) {
14817                     cselitem = false;
14818                 }
14819                 return true;
14820             }
14821                 
14822             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14823                 match = this.store.indexOf(v);
14824                 return false;
14825             }
14826             return true;
14827         }, this);
14828         
14829         if (match === false) {
14830             return true; // no more action?
14831         }
14832         // scroll to?
14833         this.view.select(match);
14834         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14835         sn.scrollIntoView(sn.dom.parentNode, false);
14836     },
14837     
14838     onViewScroll : function(e, t){
14839         
14840         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){
14841             return;
14842         }
14843         
14844         this.hasQuery = true;
14845         
14846         this.loading = this.list.select('.loading', true).first();
14847         
14848         if(this.loading === null){
14849             this.list.createChild({
14850                 tag: 'div',
14851                 cls: 'loading roo-select2-more-results roo-select2-active',
14852                 html: 'Loading more results...'
14853             });
14854             
14855             this.loading = this.list.select('.loading', true).first();
14856             
14857             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14858             
14859             this.loading.hide();
14860         }
14861         
14862         this.loading.show();
14863         
14864         var _combo = this;
14865         
14866         this.page++;
14867         this.loadNext = true;
14868         
14869         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14870         
14871         return;
14872     },
14873     
14874     addItem : function(o)
14875     {   
14876         var dv = ''; // display value
14877         
14878         if (this.displayField) {
14879             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14880         } else {
14881             // this is an error condition!!!
14882             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14883         }
14884         
14885         if(!dv.length){
14886             return;
14887         }
14888         
14889         var choice = this.choices.createChild({
14890             tag: 'li',
14891             cls: 'roo-select2-search-choice',
14892             cn: [
14893                 {
14894                     tag: 'div',
14895                     html: dv
14896                 },
14897                 {
14898                     tag: 'a',
14899                     href: '#',
14900                     cls: 'roo-select2-search-choice-close fa fa-times',
14901                     tabindex: '-1'
14902                 }
14903             ]
14904             
14905         }, this.searchField);
14906         
14907         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14908         
14909         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14910         
14911         this.item.push(o);
14912         
14913         this.lastData = o;
14914         
14915         this.syncValue();
14916         
14917         this.inputEl().dom.value = '';
14918         
14919         this.validate();
14920     },
14921     
14922     onRemoveItem : function(e, _self, o)
14923     {
14924         e.preventDefault();
14925         
14926         this.lastItem = Roo.apply([], this.item);
14927         
14928         var index = this.item.indexOf(o.data) * 1;
14929         
14930         if( index < 0){
14931             Roo.log('not this item?!');
14932             return;
14933         }
14934         
14935         this.item.splice(index, 1);
14936         o.item.remove();
14937         
14938         this.syncValue();
14939         
14940         this.fireEvent('remove', this, e);
14941         
14942         this.validate();
14943         
14944     },
14945     
14946     syncValue : function()
14947     {
14948         if(!this.item.length){
14949             this.clearValue();
14950             return;
14951         }
14952             
14953         var value = [];
14954         var _this = this;
14955         Roo.each(this.item, function(i){
14956             if(_this.valueField){
14957                 value.push(i[_this.valueField]);
14958                 return;
14959             }
14960
14961             value.push(i);
14962         });
14963
14964         this.value = value.join(',');
14965
14966         if(this.hiddenField){
14967             this.hiddenField.dom.value = this.value;
14968         }
14969         
14970         this.store.fireEvent("datachanged", this.store);
14971         
14972         this.validate();
14973     },
14974     
14975     clearItem : function()
14976     {
14977         if(!this.multiple){
14978             return;
14979         }
14980         
14981         this.item = [];
14982         
14983         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14984            c.remove();
14985         });
14986         
14987         this.syncValue();
14988         
14989         this.validate();
14990         
14991         if(this.tickable && !Roo.isTouch){
14992             this.view.refresh();
14993         }
14994     },
14995     
14996     inputEl: function ()
14997     {
14998         if(Roo.isIOS && this.useNativeIOS){
14999             return this.el.select('select.roo-ios-select', true).first();
15000         }
15001         
15002         if(Roo.isTouch && this.mobileTouchView){
15003             return this.el.select('input.form-control',true).first();
15004         }
15005         
15006         if(this.tickable){
15007             return this.searchField;
15008         }
15009         
15010         return this.el.select('input.form-control',true).first();
15011     },
15012     
15013     onTickableFooterButtonClick : function(e, btn, el)
15014     {
15015         e.preventDefault();
15016         
15017         this.lastItem = Roo.apply([], this.item);
15018         
15019         if(btn && btn.name == 'cancel'){
15020             this.tickItems = Roo.apply([], this.item);
15021             this.collapse();
15022             return;
15023         }
15024         
15025         this.clearItem();
15026         
15027         var _this = this;
15028         
15029         Roo.each(this.tickItems, function(o){
15030             _this.addItem(o);
15031         });
15032         
15033         this.collapse();
15034         
15035     },
15036     
15037     validate : function()
15038     {
15039         if(this.getVisibilityEl().hasClass('hidden')){
15040             return true;
15041         }
15042         
15043         var v = this.getRawValue();
15044         
15045         if(this.multiple){
15046             v = this.getValue();
15047         }
15048         
15049         if(this.disabled || this.allowBlank || v.length){
15050             this.markValid();
15051             return true;
15052         }
15053         
15054         this.markInvalid();
15055         return false;
15056     },
15057     
15058     tickableInputEl : function()
15059     {
15060         if(!this.tickable || !this.editable){
15061             return this.inputEl();
15062         }
15063         
15064         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15065     },
15066     
15067     
15068     getAutoCreateTouchView : function()
15069     {
15070         var id = Roo.id();
15071         
15072         var cfg = {
15073             cls: 'form-group' //input-group
15074         };
15075         
15076         var input =  {
15077             tag: 'input',
15078             id : id,
15079             type : this.inputType,
15080             cls : 'form-control x-combo-noedit',
15081             autocomplete: 'new-password',
15082             placeholder : this.placeholder || '',
15083             readonly : true
15084         };
15085         
15086         if (this.name) {
15087             input.name = this.name;
15088         }
15089         
15090         if (this.size) {
15091             input.cls += ' input-' + this.size;
15092         }
15093         
15094         if (this.disabled) {
15095             input.disabled = true;
15096         }
15097         
15098         var inputblock = {
15099             cls : '',
15100             cn : [
15101                 input
15102             ]
15103         };
15104         
15105         if(this.before){
15106             inputblock.cls += ' input-group';
15107             
15108             inputblock.cn.unshift({
15109                 tag :'span',
15110                 cls : 'input-group-addon input-group-prepend input-group-text',
15111                 html : this.before
15112             });
15113         }
15114         
15115         if(this.removable && !this.multiple){
15116             inputblock.cls += ' roo-removable';
15117             
15118             inputblock.cn.push({
15119                 tag: 'button',
15120                 html : 'x',
15121                 cls : 'roo-combo-removable-btn close'
15122             });
15123         }
15124
15125         if(this.hasFeedback && !this.allowBlank){
15126             
15127             inputblock.cls += ' has-feedback';
15128             
15129             inputblock.cn.push({
15130                 tag: 'span',
15131                 cls: 'glyphicon form-control-feedback'
15132             });
15133             
15134         }
15135         
15136         if (this.after) {
15137             
15138             inputblock.cls += (this.before) ? '' : ' input-group';
15139             
15140             inputblock.cn.push({
15141                 tag :'span',
15142                 cls : 'input-group-addon input-group-append input-group-text',
15143                 html : this.after
15144             });
15145         }
15146
15147         
15148         var ibwrap = inputblock;
15149         
15150         if(this.multiple){
15151             ibwrap = {
15152                 tag: 'ul',
15153                 cls: 'roo-select2-choices',
15154                 cn:[
15155                     {
15156                         tag: 'li',
15157                         cls: 'roo-select2-search-field',
15158                         cn: [
15159
15160                             inputblock
15161                         ]
15162                     }
15163                 ]
15164             };
15165         
15166             
15167         }
15168         
15169         var combobox = {
15170             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15171             cn: [
15172                 {
15173                     tag: 'input',
15174                     type : 'hidden',
15175                     cls: 'form-hidden-field'
15176                 },
15177                 ibwrap
15178             ]
15179         };
15180         
15181         if(!this.multiple && this.showToggleBtn){
15182             
15183             var caret = {
15184                         tag: 'span',
15185                         cls: 'caret'
15186             };
15187             
15188             if (this.caret != false) {
15189                 caret = {
15190                      tag: 'i',
15191                      cls: 'fa fa-' + this.caret
15192                 };
15193                 
15194             }
15195             
15196             combobox.cn.push({
15197                 tag :'span',
15198                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15199                 cn : [
15200                     caret,
15201                     {
15202                         tag: 'span',
15203                         cls: 'combobox-clear',
15204                         cn  : [
15205                             {
15206                                 tag : 'i',
15207                                 cls: 'icon-remove'
15208                             }
15209                         ]
15210                     }
15211                 ]
15212
15213             })
15214         }
15215         
15216         if(this.multiple){
15217             combobox.cls += ' roo-select2-container-multi';
15218         }
15219         
15220         var align = this.labelAlign || this.parentLabelAlign();
15221         
15222         if (align ==='left' && this.fieldLabel.length) {
15223
15224             cfg.cn = [
15225                 {
15226                    tag : 'i',
15227                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15228                    tooltip : 'This field is required'
15229                 },
15230                 {
15231                     tag: 'label',
15232                     cls : 'control-label col-form-label',
15233                     html : this.fieldLabel
15234
15235                 },
15236                 {
15237                     cls : '', 
15238                     cn: [
15239                         combobox
15240                     ]
15241                 }
15242             ];
15243             
15244             var labelCfg = cfg.cn[1];
15245             var contentCfg = cfg.cn[2];
15246             
15247
15248             if(this.indicatorpos == 'right'){
15249                 cfg.cn = [
15250                     {
15251                         tag: 'label',
15252                         'for' :  id,
15253                         cls : 'control-label col-form-label',
15254                         cn : [
15255                             {
15256                                 tag : 'span',
15257                                 html : this.fieldLabel
15258                             },
15259                             {
15260                                 tag : 'i',
15261                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15262                                 tooltip : 'This field is required'
15263                             }
15264                         ]
15265                     },
15266                     {
15267                         cls : "",
15268                         cn: [
15269                             combobox
15270                         ]
15271                     }
15272
15273                 ];
15274                 
15275                 labelCfg = cfg.cn[0];
15276                 contentCfg = cfg.cn[1];
15277             }
15278             
15279            
15280             
15281             if(this.labelWidth > 12){
15282                 labelCfg.style = "width: " + this.labelWidth + 'px';
15283             }
15284             
15285             if(this.labelWidth < 13 && this.labelmd == 0){
15286                 this.labelmd = this.labelWidth;
15287             }
15288             
15289             if(this.labellg > 0){
15290                 labelCfg.cls += ' col-lg-' + this.labellg;
15291                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15292             }
15293             
15294             if(this.labelmd > 0){
15295                 labelCfg.cls += ' col-md-' + this.labelmd;
15296                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15297             }
15298             
15299             if(this.labelsm > 0){
15300                 labelCfg.cls += ' col-sm-' + this.labelsm;
15301                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15302             }
15303             
15304             if(this.labelxs > 0){
15305                 labelCfg.cls += ' col-xs-' + this.labelxs;
15306                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15307             }
15308                 
15309                 
15310         } else if ( this.fieldLabel.length) {
15311             cfg.cn = [
15312                 {
15313                    tag : 'i',
15314                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15315                    tooltip : 'This field is required'
15316                 },
15317                 {
15318                     tag: 'label',
15319                     cls : 'control-label',
15320                     html : this.fieldLabel
15321
15322                 },
15323                 {
15324                     cls : '', 
15325                     cn: [
15326                         combobox
15327                     ]
15328                 }
15329             ];
15330             
15331             if(this.indicatorpos == 'right'){
15332                 cfg.cn = [
15333                     {
15334                         tag: 'label',
15335                         cls : 'control-label',
15336                         html : this.fieldLabel,
15337                         cn : [
15338                             {
15339                                tag : 'i',
15340                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15341                                tooltip : 'This field is required'
15342                             }
15343                         ]
15344                     },
15345                     {
15346                         cls : '', 
15347                         cn: [
15348                             combobox
15349                         ]
15350                     }
15351                 ];
15352             }
15353         } else {
15354             cfg.cn = combobox;    
15355         }
15356         
15357         
15358         var settings = this;
15359         
15360         ['xs','sm','md','lg'].map(function(size){
15361             if (settings[size]) {
15362                 cfg.cls += ' col-' + size + '-' + settings[size];
15363             }
15364         });
15365         
15366         return cfg;
15367     },
15368     
15369     initTouchView : function()
15370     {
15371         this.renderTouchView();
15372         
15373         this.touchViewEl.on('scroll', function(){
15374             this.el.dom.scrollTop = 0;
15375         }, this);
15376         
15377         this.originalValue = this.getValue();
15378         
15379         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15380         
15381         this.inputEl().on("click", this.showTouchView, this);
15382         if (this.triggerEl) {
15383             this.triggerEl.on("click", this.showTouchView, this);
15384         }
15385         
15386         
15387         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15388         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15389         
15390         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15391         
15392         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15393         this.store.on('load', this.onTouchViewLoad, this);
15394         this.store.on('loadexception', this.onTouchViewLoadException, this);
15395         
15396         if(this.hiddenName){
15397             
15398             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15399             
15400             this.hiddenField.dom.value =
15401                 this.hiddenValue !== undefined ? this.hiddenValue :
15402                 this.value !== undefined ? this.value : '';
15403         
15404             this.el.dom.removeAttribute('name');
15405             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15406         }
15407         
15408         if(this.multiple){
15409             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15410             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15411         }
15412         
15413         if(this.removable && !this.multiple){
15414             var close = this.closeTriggerEl();
15415             if(close){
15416                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15417                 close.on('click', this.removeBtnClick, this, close);
15418             }
15419         }
15420         /*
15421          * fix the bug in Safari iOS8
15422          */
15423         this.inputEl().on("focus", function(e){
15424             document.activeElement.blur();
15425         }, this);
15426         
15427         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15428         
15429         return;
15430         
15431         
15432     },
15433     
15434     renderTouchView : function()
15435     {
15436         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15437         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15438         
15439         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15440         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15441         
15442         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15443         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15444         this.touchViewBodyEl.setStyle('overflow', 'auto');
15445         
15446         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15447         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15448         
15449         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15450         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15451         
15452     },
15453     
15454     showTouchView : function()
15455     {
15456         if(this.disabled){
15457             return;
15458         }
15459         
15460         this.touchViewHeaderEl.hide();
15461
15462         if(this.modalTitle.length){
15463             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15464             this.touchViewHeaderEl.show();
15465         }
15466
15467         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15468         this.touchViewEl.show();
15469
15470         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15471         
15472         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15473         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15474
15475         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15476
15477         if(this.modalTitle.length){
15478             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15479         }
15480         
15481         this.touchViewBodyEl.setHeight(bodyHeight);
15482
15483         if(this.animate){
15484             var _this = this;
15485             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15486         }else{
15487             this.touchViewEl.addClass('in');
15488         }
15489         
15490         if(this._touchViewMask){
15491             Roo.get(document.body).addClass("x-body-masked");
15492             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15493             this._touchViewMask.setStyle('z-index', 10000);
15494             this._touchViewMask.addClass('show');
15495         }
15496         
15497         this.doTouchViewQuery();
15498         
15499     },
15500     
15501     hideTouchView : function()
15502     {
15503         this.touchViewEl.removeClass('in');
15504
15505         if(this.animate){
15506             var _this = this;
15507             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15508         }else{
15509             this.touchViewEl.setStyle('display', 'none');
15510         }
15511         
15512         if(this._touchViewMask){
15513             this._touchViewMask.removeClass('show');
15514             Roo.get(document.body).removeClass("x-body-masked");
15515         }
15516     },
15517     
15518     setTouchViewValue : function()
15519     {
15520         if(this.multiple){
15521             this.clearItem();
15522         
15523             var _this = this;
15524
15525             Roo.each(this.tickItems, function(o){
15526                 this.addItem(o);
15527             }, this);
15528         }
15529         
15530         this.hideTouchView();
15531     },
15532     
15533     doTouchViewQuery : function()
15534     {
15535         var qe = {
15536             query: '',
15537             forceAll: true,
15538             combo: this,
15539             cancel:false
15540         };
15541         
15542         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15543             return false;
15544         }
15545         
15546         if(!this.alwaysQuery || this.mode == 'local'){
15547             this.onTouchViewLoad();
15548             return;
15549         }
15550         
15551         this.store.load();
15552     },
15553     
15554     onTouchViewBeforeLoad : function(combo,opts)
15555     {
15556         return;
15557     },
15558
15559     // private
15560     onTouchViewLoad : function()
15561     {
15562         if(this.store.getCount() < 1){
15563             this.onTouchViewEmptyResults();
15564             return;
15565         }
15566         
15567         this.clearTouchView();
15568         
15569         var rawValue = this.getRawValue();
15570         
15571         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15572         
15573         this.tickItems = [];
15574         
15575         this.store.data.each(function(d, rowIndex){
15576             var row = this.touchViewListGroup.createChild(template);
15577             
15578             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15579                 row.addClass(d.data.cls);
15580             }
15581             
15582             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15583                 var cfg = {
15584                     data : d.data,
15585                     html : d.data[this.displayField]
15586                 };
15587                 
15588                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15589                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15590                 }
15591             }
15592             row.removeClass('selected');
15593             if(!this.multiple && this.valueField &&
15594                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15595             {
15596                 // radio buttons..
15597                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15598                 row.addClass('selected');
15599             }
15600             
15601             if(this.multiple && this.valueField &&
15602                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15603             {
15604                 
15605                 // checkboxes...
15606                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15607                 this.tickItems.push(d.data);
15608             }
15609             
15610             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15611             
15612         }, this);
15613         
15614         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15615         
15616         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15617
15618         if(this.modalTitle.length){
15619             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15620         }
15621
15622         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15623         
15624         if(this.mobile_restrict_height && listHeight < bodyHeight){
15625             this.touchViewBodyEl.setHeight(listHeight);
15626         }
15627         
15628         var _this = this;
15629         
15630         if(firstChecked && listHeight > bodyHeight){
15631             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15632         }
15633         
15634     },
15635     
15636     onTouchViewLoadException : function()
15637     {
15638         this.hideTouchView();
15639     },
15640     
15641     onTouchViewEmptyResults : function()
15642     {
15643         this.clearTouchView();
15644         
15645         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15646         
15647         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15648         
15649     },
15650     
15651     clearTouchView : function()
15652     {
15653         this.touchViewListGroup.dom.innerHTML = '';
15654     },
15655     
15656     onTouchViewClick : function(e, el, o)
15657     {
15658         e.preventDefault();
15659         
15660         var row = o.row;
15661         var rowIndex = o.rowIndex;
15662         
15663         var r = this.store.getAt(rowIndex);
15664         
15665         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15666             
15667             if(!this.multiple){
15668                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15669                     c.dom.removeAttribute('checked');
15670                 }, this);
15671
15672                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15673
15674                 this.setFromData(r.data);
15675
15676                 var close = this.closeTriggerEl();
15677
15678                 if(close){
15679                     close.show();
15680                 }
15681
15682                 this.hideTouchView();
15683
15684                 this.fireEvent('select', this, r, rowIndex);
15685
15686                 return;
15687             }
15688
15689             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15690                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15691                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15692                 return;
15693             }
15694
15695             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15696             this.addItem(r.data);
15697             this.tickItems.push(r.data);
15698         }
15699     },
15700     
15701     getAutoCreateNativeIOS : function()
15702     {
15703         var cfg = {
15704             cls: 'form-group' //input-group,
15705         };
15706         
15707         var combobox =  {
15708             tag: 'select',
15709             cls : 'roo-ios-select'
15710         };
15711         
15712         if (this.name) {
15713             combobox.name = this.name;
15714         }
15715         
15716         if (this.disabled) {
15717             combobox.disabled = true;
15718         }
15719         
15720         var settings = this;
15721         
15722         ['xs','sm','md','lg'].map(function(size){
15723             if (settings[size]) {
15724                 cfg.cls += ' col-' + size + '-' + settings[size];
15725             }
15726         });
15727         
15728         cfg.cn = combobox;
15729         
15730         return cfg;
15731         
15732     },
15733     
15734     initIOSView : function()
15735     {
15736         this.store.on('load', this.onIOSViewLoad, this);
15737         
15738         return;
15739     },
15740     
15741     onIOSViewLoad : function()
15742     {
15743         if(this.store.getCount() < 1){
15744             return;
15745         }
15746         
15747         this.clearIOSView();
15748         
15749         if(this.allowBlank) {
15750             
15751             var default_text = '-- SELECT --';
15752             
15753             if(this.placeholder.length){
15754                 default_text = this.placeholder;
15755             }
15756             
15757             if(this.emptyTitle.length){
15758                 default_text += ' - ' + this.emptyTitle + ' -';
15759             }
15760             
15761             var opt = this.inputEl().createChild({
15762                 tag: 'option',
15763                 value : 0,
15764                 html : default_text
15765             });
15766             
15767             var o = {};
15768             o[this.valueField] = 0;
15769             o[this.displayField] = default_text;
15770             
15771             this.ios_options.push({
15772                 data : o,
15773                 el : opt
15774             });
15775             
15776         }
15777         
15778         this.store.data.each(function(d, rowIndex){
15779             
15780             var html = '';
15781             
15782             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15783                 html = d.data[this.displayField];
15784             }
15785             
15786             var value = '';
15787             
15788             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15789                 value = d.data[this.valueField];
15790             }
15791             
15792             var option = {
15793                 tag: 'option',
15794                 value : value,
15795                 html : html
15796             };
15797             
15798             if(this.value == d.data[this.valueField]){
15799                 option['selected'] = true;
15800             }
15801             
15802             var opt = this.inputEl().createChild(option);
15803             
15804             this.ios_options.push({
15805                 data : d.data,
15806                 el : opt
15807             });
15808             
15809         }, this);
15810         
15811         this.inputEl().on('change', function(){
15812            this.fireEvent('select', this);
15813         }, this);
15814         
15815     },
15816     
15817     clearIOSView: function()
15818     {
15819         this.inputEl().dom.innerHTML = '';
15820         
15821         this.ios_options = [];
15822     },
15823     
15824     setIOSValue: function(v)
15825     {
15826         this.value = v;
15827         
15828         if(!this.ios_options){
15829             return;
15830         }
15831         
15832         Roo.each(this.ios_options, function(opts){
15833            
15834            opts.el.dom.removeAttribute('selected');
15835            
15836            if(opts.data[this.valueField] != v){
15837                return;
15838            }
15839            
15840            opts.el.dom.setAttribute('selected', true);
15841            
15842         }, this);
15843     }
15844
15845     /** 
15846     * @cfg {Boolean} grow 
15847     * @hide 
15848     */
15849     /** 
15850     * @cfg {Number} growMin 
15851     * @hide 
15852     */
15853     /** 
15854     * @cfg {Number} growMax 
15855     * @hide 
15856     */
15857     /**
15858      * @hide
15859      * @method autoSize
15860      */
15861 });
15862
15863 Roo.apply(Roo.bootstrap.ComboBox,  {
15864     
15865     header : {
15866         tag: 'div',
15867         cls: 'modal-header',
15868         cn: [
15869             {
15870                 tag: 'h4',
15871                 cls: 'modal-title'
15872             }
15873         ]
15874     },
15875     
15876     body : {
15877         tag: 'div',
15878         cls: 'modal-body',
15879         cn: [
15880             {
15881                 tag: 'ul',
15882                 cls: 'list-group'
15883             }
15884         ]
15885     },
15886     
15887     listItemRadio : {
15888         tag: 'li',
15889         cls: 'list-group-item',
15890         cn: [
15891             {
15892                 tag: 'span',
15893                 cls: 'roo-combobox-list-group-item-value'
15894             },
15895             {
15896                 tag: 'div',
15897                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15898                 cn: [
15899                     {
15900                         tag: 'input',
15901                         type: 'radio'
15902                     },
15903                     {
15904                         tag: 'label'
15905                     }
15906                 ]
15907             }
15908         ]
15909     },
15910     
15911     listItemCheckbox : {
15912         tag: 'li',
15913         cls: 'list-group-item',
15914         cn: [
15915             {
15916                 tag: 'span',
15917                 cls: 'roo-combobox-list-group-item-value'
15918             },
15919             {
15920                 tag: 'div',
15921                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15922                 cn: [
15923                     {
15924                         tag: 'input',
15925                         type: 'checkbox'
15926                     },
15927                     {
15928                         tag: 'label'
15929                     }
15930                 ]
15931             }
15932         ]
15933     },
15934     
15935     emptyResult : {
15936         tag: 'div',
15937         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15938     },
15939     
15940     footer : {
15941         tag: 'div',
15942         cls: 'modal-footer',
15943         cn: [
15944             {
15945                 tag: 'div',
15946                 cls: 'row',
15947                 cn: [
15948                     {
15949                         tag: 'div',
15950                         cls: 'col-xs-6 text-left',
15951                         cn: {
15952                             tag: 'button',
15953                             cls: 'btn btn-danger roo-touch-view-cancel',
15954                             html: 'Cancel'
15955                         }
15956                     },
15957                     {
15958                         tag: 'div',
15959                         cls: 'col-xs-6 text-right',
15960                         cn: {
15961                             tag: 'button',
15962                             cls: 'btn btn-success roo-touch-view-ok',
15963                             html: 'OK'
15964                         }
15965                     }
15966                 ]
15967             }
15968         ]
15969         
15970     }
15971 });
15972
15973 Roo.apply(Roo.bootstrap.ComboBox,  {
15974     
15975     touchViewTemplate : {
15976         tag: 'div',
15977         cls: 'modal fade roo-combobox-touch-view',
15978         cn: [
15979             {
15980                 tag: 'div',
15981                 cls: 'modal-dialog',
15982                 style : 'position:fixed', // we have to fix position....
15983                 cn: [
15984                     {
15985                         tag: 'div',
15986                         cls: 'modal-content',
15987                         cn: [
15988                             Roo.bootstrap.ComboBox.header,
15989                             Roo.bootstrap.ComboBox.body,
15990                             Roo.bootstrap.ComboBox.footer
15991                         ]
15992                     }
15993                 ]
15994             }
15995         ]
15996     }
15997 });/*
15998  * Based on:
15999  * Ext JS Library 1.1.1
16000  * Copyright(c) 2006-2007, Ext JS, LLC.
16001  *
16002  * Originally Released Under LGPL - original licence link has changed is not relivant.
16003  *
16004  * Fork - LGPL
16005  * <script type="text/javascript">
16006  */
16007
16008 /**
16009  * @class Roo.View
16010  * @extends Roo.util.Observable
16011  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16012  * This class also supports single and multi selection modes. <br>
16013  * Create a data model bound view:
16014  <pre><code>
16015  var store = new Roo.data.Store(...);
16016
16017  var view = new Roo.View({
16018     el : "my-element",
16019     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16020  
16021     singleSelect: true,
16022     selectedClass: "ydataview-selected",
16023     store: store
16024  });
16025
16026  // listen for node click?
16027  view.on("click", function(vw, index, node, e){
16028  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16029  });
16030
16031  // load XML data
16032  dataModel.load("foobar.xml");
16033  </code></pre>
16034  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16035  * <br><br>
16036  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16037  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16038  * 
16039  * Note: old style constructor is still suported (container, template, config)
16040  * 
16041  * @constructor
16042  * Create a new View
16043  * @param {Object} config The config object
16044  * 
16045  */
16046 Roo.View = function(config, depreciated_tpl, depreciated_config){
16047     
16048     this.parent = false;
16049     
16050     if (typeof(depreciated_tpl) == 'undefined') {
16051         // new way.. - universal constructor.
16052         Roo.apply(this, config);
16053         this.el  = Roo.get(this.el);
16054     } else {
16055         // old format..
16056         this.el  = Roo.get(config);
16057         this.tpl = depreciated_tpl;
16058         Roo.apply(this, depreciated_config);
16059     }
16060     this.wrapEl  = this.el.wrap().wrap();
16061     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16062     
16063     
16064     if(typeof(this.tpl) == "string"){
16065         this.tpl = new Roo.Template(this.tpl);
16066     } else {
16067         // support xtype ctors..
16068         this.tpl = new Roo.factory(this.tpl, Roo);
16069     }
16070     
16071     
16072     this.tpl.compile();
16073     
16074     /** @private */
16075     this.addEvents({
16076         /**
16077          * @event beforeclick
16078          * Fires before a click is processed. Returns false to cancel the default action.
16079          * @param {Roo.View} this
16080          * @param {Number} index The index of the target node
16081          * @param {HTMLElement} node The target node
16082          * @param {Roo.EventObject} e The raw event object
16083          */
16084             "beforeclick" : true,
16085         /**
16086          * @event click
16087          * Fires when a template node is clicked.
16088          * @param {Roo.View} this
16089          * @param {Number} index The index of the target node
16090          * @param {HTMLElement} node The target node
16091          * @param {Roo.EventObject} e The raw event object
16092          */
16093             "click" : true,
16094         /**
16095          * @event dblclick
16096          * Fires when a template node is double clicked.
16097          * @param {Roo.View} this
16098          * @param {Number} index The index of the target node
16099          * @param {HTMLElement} node The target node
16100          * @param {Roo.EventObject} e The raw event object
16101          */
16102             "dblclick" : true,
16103         /**
16104          * @event contextmenu
16105          * Fires when a template node is right clicked.
16106          * @param {Roo.View} this
16107          * @param {Number} index The index of the target node
16108          * @param {HTMLElement} node The target node
16109          * @param {Roo.EventObject} e The raw event object
16110          */
16111             "contextmenu" : true,
16112         /**
16113          * @event selectionchange
16114          * Fires when the selected nodes change.
16115          * @param {Roo.View} this
16116          * @param {Array} selections Array of the selected nodes
16117          */
16118             "selectionchange" : true,
16119     
16120         /**
16121          * @event beforeselect
16122          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16123          * @param {Roo.View} this
16124          * @param {HTMLElement} node The node to be selected
16125          * @param {Array} selections Array of currently selected nodes
16126          */
16127             "beforeselect" : true,
16128         /**
16129          * @event preparedata
16130          * Fires on every row to render, to allow you to change the data.
16131          * @param {Roo.View} this
16132          * @param {Object} data to be rendered (change this)
16133          */
16134           "preparedata" : true
16135           
16136           
16137         });
16138
16139
16140
16141     this.el.on({
16142         "click": this.onClick,
16143         "dblclick": this.onDblClick,
16144         "contextmenu": this.onContextMenu,
16145         scope:this
16146     });
16147
16148     this.selections = [];
16149     this.nodes = [];
16150     this.cmp = new Roo.CompositeElementLite([]);
16151     if(this.store){
16152         this.store = Roo.factory(this.store, Roo.data);
16153         this.setStore(this.store, true);
16154     }
16155     
16156     if ( this.footer && this.footer.xtype) {
16157            
16158          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16159         
16160         this.footer.dataSource = this.store;
16161         this.footer.container = fctr;
16162         this.footer = Roo.factory(this.footer, Roo);
16163         fctr.insertFirst(this.el);
16164         
16165         // this is a bit insane - as the paging toolbar seems to detach the el..
16166 //        dom.parentNode.parentNode.parentNode
16167          // they get detached?
16168     }
16169     
16170     
16171     Roo.View.superclass.constructor.call(this);
16172     
16173     
16174 };
16175
16176 Roo.extend(Roo.View, Roo.util.Observable, {
16177     
16178      /**
16179      * @cfg {Roo.data.Store} store Data store to load data from.
16180      */
16181     store : false,
16182     
16183     /**
16184      * @cfg {String|Roo.Element} el The container element.
16185      */
16186     el : '',
16187     
16188     /**
16189      * @cfg {String|Roo.Template} tpl The template used by this View 
16190      */
16191     tpl : false,
16192     /**
16193      * @cfg {String} dataName the named area of the template to use as the data area
16194      *                          Works with domtemplates roo-name="name"
16195      */
16196     dataName: false,
16197     /**
16198      * @cfg {String} selectedClass The css class to add to selected nodes
16199      */
16200     selectedClass : "x-view-selected",
16201      /**
16202      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16203      */
16204     emptyText : "",
16205     
16206     /**
16207      * @cfg {String} text to display on mask (default Loading)
16208      */
16209     mask : false,
16210     /**
16211      * @cfg {Boolean} multiSelect Allow multiple selection
16212      */
16213     multiSelect : false,
16214     /**
16215      * @cfg {Boolean} singleSelect Allow single selection
16216      */
16217     singleSelect:  false,
16218     
16219     /**
16220      * @cfg {Boolean} toggleSelect - selecting 
16221      */
16222     toggleSelect : false,
16223     
16224     /**
16225      * @cfg {Boolean} tickable - selecting 
16226      */
16227     tickable : false,
16228     
16229     /**
16230      * Returns the element this view is bound to.
16231      * @return {Roo.Element}
16232      */
16233     getEl : function(){
16234         return this.wrapEl;
16235     },
16236     
16237     
16238
16239     /**
16240      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16241      */
16242     refresh : function(){
16243         //Roo.log('refresh');
16244         var t = this.tpl;
16245         
16246         // if we are using something like 'domtemplate', then
16247         // the what gets used is:
16248         // t.applySubtemplate(NAME, data, wrapping data..)
16249         // the outer template then get' applied with
16250         //     the store 'extra data'
16251         // and the body get's added to the
16252         //      roo-name="data" node?
16253         //      <span class='roo-tpl-{name}'></span> ?????
16254         
16255         
16256         
16257         this.clearSelections();
16258         this.el.update("");
16259         var html = [];
16260         var records = this.store.getRange();
16261         if(records.length < 1) {
16262             
16263             // is this valid??  = should it render a template??
16264             
16265             this.el.update(this.emptyText);
16266             return;
16267         }
16268         var el = this.el;
16269         if (this.dataName) {
16270             this.el.update(t.apply(this.store.meta)); //????
16271             el = this.el.child('.roo-tpl-' + this.dataName);
16272         }
16273         
16274         for(var i = 0, len = records.length; i < len; i++){
16275             var data = this.prepareData(records[i].data, i, records[i]);
16276             this.fireEvent("preparedata", this, data, i, records[i]);
16277             
16278             var d = Roo.apply({}, data);
16279             
16280             if(this.tickable){
16281                 Roo.apply(d, {'roo-id' : Roo.id()});
16282                 
16283                 var _this = this;
16284             
16285                 Roo.each(this.parent.item, function(item){
16286                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16287                         return;
16288                     }
16289                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16290                 });
16291             }
16292             
16293             html[html.length] = Roo.util.Format.trim(
16294                 this.dataName ?
16295                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16296                     t.apply(d)
16297             );
16298         }
16299         
16300         
16301         
16302         el.update(html.join(""));
16303         this.nodes = el.dom.childNodes;
16304         this.updateIndexes(0);
16305     },
16306     
16307
16308     /**
16309      * Function to override to reformat the data that is sent to
16310      * the template for each node.
16311      * DEPRICATED - use the preparedata event handler.
16312      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16313      * a JSON object for an UpdateManager bound view).
16314      */
16315     prepareData : function(data, index, record)
16316     {
16317         this.fireEvent("preparedata", this, data, index, record);
16318         return data;
16319     },
16320
16321     onUpdate : function(ds, record){
16322         // Roo.log('on update');   
16323         this.clearSelections();
16324         var index = this.store.indexOf(record);
16325         var n = this.nodes[index];
16326         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16327         n.parentNode.removeChild(n);
16328         this.updateIndexes(index, index);
16329     },
16330
16331     
16332     
16333 // --------- FIXME     
16334     onAdd : function(ds, records, index)
16335     {
16336         //Roo.log(['on Add', ds, records, index] );        
16337         this.clearSelections();
16338         if(this.nodes.length == 0){
16339             this.refresh();
16340             return;
16341         }
16342         var n = this.nodes[index];
16343         for(var i = 0, len = records.length; i < len; i++){
16344             var d = this.prepareData(records[i].data, i, records[i]);
16345             if(n){
16346                 this.tpl.insertBefore(n, d);
16347             }else{
16348                 
16349                 this.tpl.append(this.el, d);
16350             }
16351         }
16352         this.updateIndexes(index);
16353     },
16354
16355     onRemove : function(ds, record, index){
16356        // Roo.log('onRemove');
16357         this.clearSelections();
16358         var el = this.dataName  ?
16359             this.el.child('.roo-tpl-' + this.dataName) :
16360             this.el; 
16361         
16362         el.dom.removeChild(this.nodes[index]);
16363         this.updateIndexes(index);
16364     },
16365
16366     /**
16367      * Refresh an individual node.
16368      * @param {Number} index
16369      */
16370     refreshNode : function(index){
16371         this.onUpdate(this.store, this.store.getAt(index));
16372     },
16373
16374     updateIndexes : function(startIndex, endIndex){
16375         var ns = this.nodes;
16376         startIndex = startIndex || 0;
16377         endIndex = endIndex || ns.length - 1;
16378         for(var i = startIndex; i <= endIndex; i++){
16379             ns[i].nodeIndex = i;
16380         }
16381     },
16382
16383     /**
16384      * Changes the data store this view uses and refresh the view.
16385      * @param {Store} store
16386      */
16387     setStore : function(store, initial){
16388         if(!initial && this.store){
16389             this.store.un("datachanged", this.refresh);
16390             this.store.un("add", this.onAdd);
16391             this.store.un("remove", this.onRemove);
16392             this.store.un("update", this.onUpdate);
16393             this.store.un("clear", this.refresh);
16394             this.store.un("beforeload", this.onBeforeLoad);
16395             this.store.un("load", this.onLoad);
16396             this.store.un("loadexception", this.onLoad);
16397         }
16398         if(store){
16399           
16400             store.on("datachanged", this.refresh, this);
16401             store.on("add", this.onAdd, this);
16402             store.on("remove", this.onRemove, this);
16403             store.on("update", this.onUpdate, this);
16404             store.on("clear", this.refresh, this);
16405             store.on("beforeload", this.onBeforeLoad, this);
16406             store.on("load", this.onLoad, this);
16407             store.on("loadexception", this.onLoad, this);
16408         }
16409         
16410         if(store){
16411             this.refresh();
16412         }
16413     },
16414     /**
16415      * onbeforeLoad - masks the loading area.
16416      *
16417      */
16418     onBeforeLoad : function(store,opts)
16419     {
16420          //Roo.log('onBeforeLoad');   
16421         if (!opts.add) {
16422             this.el.update("");
16423         }
16424         this.el.mask(this.mask ? this.mask : "Loading" ); 
16425     },
16426     onLoad : function ()
16427     {
16428         this.el.unmask();
16429     },
16430     
16431
16432     /**
16433      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16434      * @param {HTMLElement} node
16435      * @return {HTMLElement} The template node
16436      */
16437     findItemFromChild : function(node){
16438         var el = this.dataName  ?
16439             this.el.child('.roo-tpl-' + this.dataName,true) :
16440             this.el.dom; 
16441         
16442         if(!node || node.parentNode == el){
16443                     return node;
16444             }
16445             var p = node.parentNode;
16446             while(p && p != el){
16447             if(p.parentNode == el){
16448                 return p;
16449             }
16450             p = p.parentNode;
16451         }
16452             return null;
16453     },
16454
16455     /** @ignore */
16456     onClick : function(e){
16457         var item = this.findItemFromChild(e.getTarget());
16458         if(item){
16459             var index = this.indexOf(item);
16460             if(this.onItemClick(item, index, e) !== false){
16461                 this.fireEvent("click", this, index, item, e);
16462             }
16463         }else{
16464             this.clearSelections();
16465         }
16466     },
16467
16468     /** @ignore */
16469     onContextMenu : function(e){
16470         var item = this.findItemFromChild(e.getTarget());
16471         if(item){
16472             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16473         }
16474     },
16475
16476     /** @ignore */
16477     onDblClick : function(e){
16478         var item = this.findItemFromChild(e.getTarget());
16479         if(item){
16480             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16481         }
16482     },
16483
16484     onItemClick : function(item, index, e)
16485     {
16486         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16487             return false;
16488         }
16489         if (this.toggleSelect) {
16490             var m = this.isSelected(item) ? 'unselect' : 'select';
16491             //Roo.log(m);
16492             var _t = this;
16493             _t[m](item, true, false);
16494             return true;
16495         }
16496         if(this.multiSelect || this.singleSelect){
16497             if(this.multiSelect && e.shiftKey && this.lastSelection){
16498                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16499             }else{
16500                 this.select(item, this.multiSelect && e.ctrlKey);
16501                 this.lastSelection = item;
16502             }
16503             
16504             if(!this.tickable){
16505                 e.preventDefault();
16506             }
16507             
16508         }
16509         return true;
16510     },
16511
16512     /**
16513      * Get the number of selected nodes.
16514      * @return {Number}
16515      */
16516     getSelectionCount : function(){
16517         return this.selections.length;
16518     },
16519
16520     /**
16521      * Get the currently selected nodes.
16522      * @return {Array} An array of HTMLElements
16523      */
16524     getSelectedNodes : function(){
16525         return this.selections;
16526     },
16527
16528     /**
16529      * Get the indexes of the selected nodes.
16530      * @return {Array}
16531      */
16532     getSelectedIndexes : function(){
16533         var indexes = [], s = this.selections;
16534         for(var i = 0, len = s.length; i < len; i++){
16535             indexes.push(s[i].nodeIndex);
16536         }
16537         return indexes;
16538     },
16539
16540     /**
16541      * Clear all selections
16542      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16543      */
16544     clearSelections : function(suppressEvent){
16545         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16546             this.cmp.elements = this.selections;
16547             this.cmp.removeClass(this.selectedClass);
16548             this.selections = [];
16549             if(!suppressEvent){
16550                 this.fireEvent("selectionchange", this, this.selections);
16551             }
16552         }
16553     },
16554
16555     /**
16556      * Returns true if the passed node is selected
16557      * @param {HTMLElement/Number} node The node or node index
16558      * @return {Boolean}
16559      */
16560     isSelected : function(node){
16561         var s = this.selections;
16562         if(s.length < 1){
16563             return false;
16564         }
16565         node = this.getNode(node);
16566         return s.indexOf(node) !== -1;
16567     },
16568
16569     /**
16570      * Selects nodes.
16571      * @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
16572      * @param {Boolean} keepExisting (optional) true to keep existing selections
16573      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16574      */
16575     select : function(nodeInfo, keepExisting, suppressEvent){
16576         if(nodeInfo instanceof Array){
16577             if(!keepExisting){
16578                 this.clearSelections(true);
16579             }
16580             for(var i = 0, len = nodeInfo.length; i < len; i++){
16581                 this.select(nodeInfo[i], true, true);
16582             }
16583             return;
16584         } 
16585         var node = this.getNode(nodeInfo);
16586         if(!node || this.isSelected(node)){
16587             return; // already selected.
16588         }
16589         if(!keepExisting){
16590             this.clearSelections(true);
16591         }
16592         
16593         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16594             Roo.fly(node).addClass(this.selectedClass);
16595             this.selections.push(node);
16596             if(!suppressEvent){
16597                 this.fireEvent("selectionchange", this, this.selections);
16598             }
16599         }
16600         
16601         
16602     },
16603       /**
16604      * Unselects nodes.
16605      * @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
16606      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16607      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16608      */
16609     unselect : function(nodeInfo, keepExisting, suppressEvent)
16610     {
16611         if(nodeInfo instanceof Array){
16612             Roo.each(this.selections, function(s) {
16613                 this.unselect(s, nodeInfo);
16614             }, this);
16615             return;
16616         }
16617         var node = this.getNode(nodeInfo);
16618         if(!node || !this.isSelected(node)){
16619             //Roo.log("not selected");
16620             return; // not selected.
16621         }
16622         // fireevent???
16623         var ns = [];
16624         Roo.each(this.selections, function(s) {
16625             if (s == node ) {
16626                 Roo.fly(node).removeClass(this.selectedClass);
16627
16628                 return;
16629             }
16630             ns.push(s);
16631         },this);
16632         
16633         this.selections= ns;
16634         this.fireEvent("selectionchange", this, this.selections);
16635     },
16636
16637     /**
16638      * Gets a template node.
16639      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16640      * @return {HTMLElement} The node or null if it wasn't found
16641      */
16642     getNode : function(nodeInfo){
16643         if(typeof nodeInfo == "string"){
16644             return document.getElementById(nodeInfo);
16645         }else if(typeof nodeInfo == "number"){
16646             return this.nodes[nodeInfo];
16647         }
16648         return nodeInfo;
16649     },
16650
16651     /**
16652      * Gets a range template nodes.
16653      * @param {Number} startIndex
16654      * @param {Number} endIndex
16655      * @return {Array} An array of nodes
16656      */
16657     getNodes : function(start, end){
16658         var ns = this.nodes;
16659         start = start || 0;
16660         end = typeof end == "undefined" ? ns.length - 1 : end;
16661         var nodes = [];
16662         if(start <= end){
16663             for(var i = start; i <= end; i++){
16664                 nodes.push(ns[i]);
16665             }
16666         } else{
16667             for(var i = start; i >= end; i--){
16668                 nodes.push(ns[i]);
16669             }
16670         }
16671         return nodes;
16672     },
16673
16674     /**
16675      * Finds the index of the passed node
16676      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16677      * @return {Number} The index of the node or -1
16678      */
16679     indexOf : function(node){
16680         node = this.getNode(node);
16681         if(typeof node.nodeIndex == "number"){
16682             return node.nodeIndex;
16683         }
16684         var ns = this.nodes;
16685         for(var i = 0, len = ns.length; i < len; i++){
16686             if(ns[i] == node){
16687                 return i;
16688             }
16689         }
16690         return -1;
16691     }
16692 });
16693 /*
16694  * - LGPL
16695  *
16696  * based on jquery fullcalendar
16697  * 
16698  */
16699
16700 Roo.bootstrap = Roo.bootstrap || {};
16701 /**
16702  * @class Roo.bootstrap.Calendar
16703  * @extends Roo.bootstrap.Component
16704  * Bootstrap Calendar class
16705  * @cfg {Boolean} loadMask (true|false) default false
16706  * @cfg {Object} header generate the user specific header of the calendar, default false
16707
16708  * @constructor
16709  * Create a new Container
16710  * @param {Object} config The config object
16711  */
16712
16713
16714
16715 Roo.bootstrap.Calendar = function(config){
16716     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16717      this.addEvents({
16718         /**
16719              * @event select
16720              * Fires when a date is selected
16721              * @param {DatePicker} this
16722              * @param {Date} date The selected date
16723              */
16724         'select': true,
16725         /**
16726              * @event monthchange
16727              * Fires when the displayed month changes 
16728              * @param {DatePicker} this
16729              * @param {Date} date The selected month
16730              */
16731         'monthchange': true,
16732         /**
16733              * @event evententer
16734              * Fires when mouse over an event
16735              * @param {Calendar} this
16736              * @param {event} Event
16737              */
16738         'evententer': true,
16739         /**
16740              * @event eventleave
16741              * Fires when the mouse leaves an
16742              * @param {Calendar} this
16743              * @param {event}
16744              */
16745         'eventleave': true,
16746         /**
16747              * @event eventclick
16748              * Fires when the mouse click an
16749              * @param {Calendar} this
16750              * @param {event}
16751              */
16752         'eventclick': true
16753         
16754     });
16755
16756 };
16757
16758 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16759     
16760      /**
16761      * @cfg {Number} startDay
16762      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16763      */
16764     startDay : 0,
16765     
16766     loadMask : false,
16767     
16768     header : false,
16769       
16770     getAutoCreate : function(){
16771         
16772         
16773         var fc_button = function(name, corner, style, content ) {
16774             return Roo.apply({},{
16775                 tag : 'span',
16776                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16777                          (corner.length ?
16778                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16779                             ''
16780                         ),
16781                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16782                 unselectable: 'on'
16783             });
16784         };
16785         
16786         var header = {};
16787         
16788         if(!this.header){
16789             header = {
16790                 tag : 'table',
16791                 cls : 'fc-header',
16792                 style : 'width:100%',
16793                 cn : [
16794                     {
16795                         tag: 'tr',
16796                         cn : [
16797                             {
16798                                 tag : 'td',
16799                                 cls : 'fc-header-left',
16800                                 cn : [
16801                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16802                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16803                                     { tag: 'span', cls: 'fc-header-space' },
16804                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16805
16806
16807                                 ]
16808                             },
16809
16810                             {
16811                                 tag : 'td',
16812                                 cls : 'fc-header-center',
16813                                 cn : [
16814                                     {
16815                                         tag: 'span',
16816                                         cls: 'fc-header-title',
16817                                         cn : {
16818                                             tag: 'H2',
16819                                             html : 'month / year'
16820                                         }
16821                                     }
16822
16823                                 ]
16824                             },
16825                             {
16826                                 tag : 'td',
16827                                 cls : 'fc-header-right',
16828                                 cn : [
16829                               /*      fc_button('month', 'left', '', 'month' ),
16830                                     fc_button('week', '', '', 'week' ),
16831                                     fc_button('day', 'right', '', 'day' )
16832                                 */    
16833
16834                                 ]
16835                             }
16836
16837                         ]
16838                     }
16839                 ]
16840             };
16841         }
16842         
16843         header = this.header;
16844         
16845        
16846         var cal_heads = function() {
16847             var ret = [];
16848             // fixme - handle this.
16849             
16850             for (var i =0; i < Date.dayNames.length; i++) {
16851                 var d = Date.dayNames[i];
16852                 ret.push({
16853                     tag: 'th',
16854                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16855                     html : d.substring(0,3)
16856                 });
16857                 
16858             }
16859             ret[0].cls += ' fc-first';
16860             ret[6].cls += ' fc-last';
16861             return ret;
16862         };
16863         var cal_cell = function(n) {
16864             return  {
16865                 tag: 'td',
16866                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16867                 cn : [
16868                     {
16869                         cn : [
16870                             {
16871                                 cls: 'fc-day-number',
16872                                 html: 'D'
16873                             },
16874                             {
16875                                 cls: 'fc-day-content',
16876                              
16877                                 cn : [
16878                                      {
16879                                         style: 'position: relative;' // height: 17px;
16880                                     }
16881                                 ]
16882                             }
16883                             
16884                             
16885                         ]
16886                     }
16887                 ]
16888                 
16889             }
16890         };
16891         var cal_rows = function() {
16892             
16893             var ret = [];
16894             for (var r = 0; r < 6; r++) {
16895                 var row= {
16896                     tag : 'tr',
16897                     cls : 'fc-week',
16898                     cn : []
16899                 };
16900                 
16901                 for (var i =0; i < Date.dayNames.length; i++) {
16902                     var d = Date.dayNames[i];
16903                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16904
16905                 }
16906                 row.cn[0].cls+=' fc-first';
16907                 row.cn[0].cn[0].style = 'min-height:90px';
16908                 row.cn[6].cls+=' fc-last';
16909                 ret.push(row);
16910                 
16911             }
16912             ret[0].cls += ' fc-first';
16913             ret[4].cls += ' fc-prev-last';
16914             ret[5].cls += ' fc-last';
16915             return ret;
16916             
16917         };
16918         
16919         var cal_table = {
16920             tag: 'table',
16921             cls: 'fc-border-separate',
16922             style : 'width:100%',
16923             cellspacing  : 0,
16924             cn : [
16925                 { 
16926                     tag: 'thead',
16927                     cn : [
16928                         { 
16929                             tag: 'tr',
16930                             cls : 'fc-first fc-last',
16931                             cn : cal_heads()
16932                         }
16933                     ]
16934                 },
16935                 { 
16936                     tag: 'tbody',
16937                     cn : cal_rows()
16938                 }
16939                   
16940             ]
16941         };
16942          
16943          var cfg = {
16944             cls : 'fc fc-ltr',
16945             cn : [
16946                 header,
16947                 {
16948                     cls : 'fc-content',
16949                     style : "position: relative;",
16950                     cn : [
16951                         {
16952                             cls : 'fc-view fc-view-month fc-grid',
16953                             style : 'position: relative',
16954                             unselectable : 'on',
16955                             cn : [
16956                                 {
16957                                     cls : 'fc-event-container',
16958                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16959                                 },
16960                                 cal_table
16961                             ]
16962                         }
16963                     ]
16964     
16965                 }
16966            ] 
16967             
16968         };
16969         
16970          
16971         
16972         return cfg;
16973     },
16974     
16975     
16976     initEvents : function()
16977     {
16978         if(!this.store){
16979             throw "can not find store for calendar";
16980         }
16981         
16982         var mark = {
16983             tag: "div",
16984             cls:"x-dlg-mask",
16985             style: "text-align:center",
16986             cn: [
16987                 {
16988                     tag: "div",
16989                     style: "background-color:white;width:50%;margin:250 auto",
16990                     cn: [
16991                         {
16992                             tag: "img",
16993                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16994                         },
16995                         {
16996                             tag: "span",
16997                             html: "Loading"
16998                         }
16999                         
17000                     ]
17001                 }
17002             ]
17003         };
17004         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17005         
17006         var size = this.el.select('.fc-content', true).first().getSize();
17007         this.maskEl.setSize(size.width, size.height);
17008         this.maskEl.enableDisplayMode("block");
17009         if(!this.loadMask){
17010             this.maskEl.hide();
17011         }
17012         
17013         this.store = Roo.factory(this.store, Roo.data);
17014         this.store.on('load', this.onLoad, this);
17015         this.store.on('beforeload', this.onBeforeLoad, this);
17016         
17017         this.resize();
17018         
17019         this.cells = this.el.select('.fc-day',true);
17020         //Roo.log(this.cells);
17021         this.textNodes = this.el.query('.fc-day-number');
17022         this.cells.addClassOnOver('fc-state-hover');
17023         
17024         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17025         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17026         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17027         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17028         
17029         this.on('monthchange', this.onMonthChange, this);
17030         
17031         this.update(new Date().clearTime());
17032     },
17033     
17034     resize : function() {
17035         var sz  = this.el.getSize();
17036         
17037         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17038         this.el.select('.fc-day-content div',true).setHeight(34);
17039     },
17040     
17041     
17042     // private
17043     showPrevMonth : function(e){
17044         this.update(this.activeDate.add("mo", -1));
17045     },
17046     showToday : function(e){
17047         this.update(new Date().clearTime());
17048     },
17049     // private
17050     showNextMonth : function(e){
17051         this.update(this.activeDate.add("mo", 1));
17052     },
17053
17054     // private
17055     showPrevYear : function(){
17056         this.update(this.activeDate.add("y", -1));
17057     },
17058
17059     // private
17060     showNextYear : function(){
17061         this.update(this.activeDate.add("y", 1));
17062     },
17063
17064     
17065    // private
17066     update : function(date)
17067     {
17068         var vd = this.activeDate;
17069         this.activeDate = date;
17070 //        if(vd && this.el){
17071 //            var t = date.getTime();
17072 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17073 //                Roo.log('using add remove');
17074 //                
17075 //                this.fireEvent('monthchange', this, date);
17076 //                
17077 //                this.cells.removeClass("fc-state-highlight");
17078 //                this.cells.each(function(c){
17079 //                   if(c.dateValue == t){
17080 //                       c.addClass("fc-state-highlight");
17081 //                       setTimeout(function(){
17082 //                            try{c.dom.firstChild.focus();}catch(e){}
17083 //                       }, 50);
17084 //                       return false;
17085 //                   }
17086 //                   return true;
17087 //                });
17088 //                return;
17089 //            }
17090 //        }
17091         
17092         var days = date.getDaysInMonth();
17093         
17094         var firstOfMonth = date.getFirstDateOfMonth();
17095         var startingPos = firstOfMonth.getDay()-this.startDay;
17096         
17097         if(startingPos < this.startDay){
17098             startingPos += 7;
17099         }
17100         
17101         var pm = date.add(Date.MONTH, -1);
17102         var prevStart = pm.getDaysInMonth()-startingPos;
17103 //        
17104         this.cells = this.el.select('.fc-day',true);
17105         this.textNodes = this.el.query('.fc-day-number');
17106         this.cells.addClassOnOver('fc-state-hover');
17107         
17108         var cells = this.cells.elements;
17109         var textEls = this.textNodes;
17110         
17111         Roo.each(cells, function(cell){
17112             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17113         });
17114         
17115         days += startingPos;
17116
17117         // convert everything to numbers so it's fast
17118         var day = 86400000;
17119         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17120         //Roo.log(d);
17121         //Roo.log(pm);
17122         //Roo.log(prevStart);
17123         
17124         var today = new Date().clearTime().getTime();
17125         var sel = date.clearTime().getTime();
17126         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17127         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17128         var ddMatch = this.disabledDatesRE;
17129         var ddText = this.disabledDatesText;
17130         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17131         var ddaysText = this.disabledDaysText;
17132         var format = this.format;
17133         
17134         var setCellClass = function(cal, cell){
17135             cell.row = 0;
17136             cell.events = [];
17137             cell.more = [];
17138             //Roo.log('set Cell Class');
17139             cell.title = "";
17140             var t = d.getTime();
17141             
17142             //Roo.log(d);
17143             
17144             cell.dateValue = t;
17145             if(t == today){
17146                 cell.className += " fc-today";
17147                 cell.className += " fc-state-highlight";
17148                 cell.title = cal.todayText;
17149             }
17150             if(t == sel){
17151                 // disable highlight in other month..
17152                 //cell.className += " fc-state-highlight";
17153                 
17154             }
17155             // disabling
17156             if(t < min) {
17157                 cell.className = " fc-state-disabled";
17158                 cell.title = cal.minText;
17159                 return;
17160             }
17161             if(t > max) {
17162                 cell.className = " fc-state-disabled";
17163                 cell.title = cal.maxText;
17164                 return;
17165             }
17166             if(ddays){
17167                 if(ddays.indexOf(d.getDay()) != -1){
17168                     cell.title = ddaysText;
17169                     cell.className = " fc-state-disabled";
17170                 }
17171             }
17172             if(ddMatch && format){
17173                 var fvalue = d.dateFormat(format);
17174                 if(ddMatch.test(fvalue)){
17175                     cell.title = ddText.replace("%0", fvalue);
17176                     cell.className = " fc-state-disabled";
17177                 }
17178             }
17179             
17180             if (!cell.initialClassName) {
17181                 cell.initialClassName = cell.dom.className;
17182             }
17183             
17184             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17185         };
17186
17187         var i = 0;
17188         
17189         for(; i < startingPos; i++) {
17190             textEls[i].innerHTML = (++prevStart);
17191             d.setDate(d.getDate()+1);
17192             
17193             cells[i].className = "fc-past fc-other-month";
17194             setCellClass(this, cells[i]);
17195         }
17196         
17197         var intDay = 0;
17198         
17199         for(; i < days; i++){
17200             intDay = i - startingPos + 1;
17201             textEls[i].innerHTML = (intDay);
17202             d.setDate(d.getDate()+1);
17203             
17204             cells[i].className = ''; // "x-date-active";
17205             setCellClass(this, cells[i]);
17206         }
17207         var extraDays = 0;
17208         
17209         for(; i < 42; i++) {
17210             textEls[i].innerHTML = (++extraDays);
17211             d.setDate(d.getDate()+1);
17212             
17213             cells[i].className = "fc-future fc-other-month";
17214             setCellClass(this, cells[i]);
17215         }
17216         
17217         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17218         
17219         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17220         
17221         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17222         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17223         
17224         if(totalRows != 6){
17225             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17226             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17227         }
17228         
17229         this.fireEvent('monthchange', this, date);
17230         
17231         
17232         /*
17233         if(!this.internalRender){
17234             var main = this.el.dom.firstChild;
17235             var w = main.offsetWidth;
17236             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17237             Roo.fly(main).setWidth(w);
17238             this.internalRender = true;
17239             // opera does not respect the auto grow header center column
17240             // then, after it gets a width opera refuses to recalculate
17241             // without a second pass
17242             if(Roo.isOpera && !this.secondPass){
17243                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17244                 this.secondPass = true;
17245                 this.update.defer(10, this, [date]);
17246             }
17247         }
17248         */
17249         
17250     },
17251     
17252     findCell : function(dt) {
17253         dt = dt.clearTime().getTime();
17254         var ret = false;
17255         this.cells.each(function(c){
17256             //Roo.log("check " +c.dateValue + '?=' + dt);
17257             if(c.dateValue == dt){
17258                 ret = c;
17259                 return false;
17260             }
17261             return true;
17262         });
17263         
17264         return ret;
17265     },
17266     
17267     findCells : function(ev) {
17268         var s = ev.start.clone().clearTime().getTime();
17269        // Roo.log(s);
17270         var e= ev.end.clone().clearTime().getTime();
17271        // Roo.log(e);
17272         var ret = [];
17273         this.cells.each(function(c){
17274              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17275             
17276             if(c.dateValue > e){
17277                 return ;
17278             }
17279             if(c.dateValue < s){
17280                 return ;
17281             }
17282             ret.push(c);
17283         });
17284         
17285         return ret;    
17286     },
17287     
17288 //    findBestRow: function(cells)
17289 //    {
17290 //        var ret = 0;
17291 //        
17292 //        for (var i =0 ; i < cells.length;i++) {
17293 //            ret  = Math.max(cells[i].rows || 0,ret);
17294 //        }
17295 //        return ret;
17296 //        
17297 //    },
17298     
17299     
17300     addItem : function(ev)
17301     {
17302         // look for vertical location slot in
17303         var cells = this.findCells(ev);
17304         
17305 //        ev.row = this.findBestRow(cells);
17306         
17307         // work out the location.
17308         
17309         var crow = false;
17310         var rows = [];
17311         for(var i =0; i < cells.length; i++) {
17312             
17313             cells[i].row = cells[0].row;
17314             
17315             if(i == 0){
17316                 cells[i].row = cells[i].row + 1;
17317             }
17318             
17319             if (!crow) {
17320                 crow = {
17321                     start : cells[i],
17322                     end :  cells[i]
17323                 };
17324                 continue;
17325             }
17326             if (crow.start.getY() == cells[i].getY()) {
17327                 // on same row.
17328                 crow.end = cells[i];
17329                 continue;
17330             }
17331             // different row.
17332             rows.push(crow);
17333             crow = {
17334                 start: cells[i],
17335                 end : cells[i]
17336             };
17337             
17338         }
17339         
17340         rows.push(crow);
17341         ev.els = [];
17342         ev.rows = rows;
17343         ev.cells = cells;
17344         
17345         cells[0].events.push(ev);
17346         
17347         this.calevents.push(ev);
17348     },
17349     
17350     clearEvents: function() {
17351         
17352         if(!this.calevents){
17353             return;
17354         }
17355         
17356         Roo.each(this.cells.elements, function(c){
17357             c.row = 0;
17358             c.events = [];
17359             c.more = [];
17360         });
17361         
17362         Roo.each(this.calevents, function(e) {
17363             Roo.each(e.els, function(el) {
17364                 el.un('mouseenter' ,this.onEventEnter, this);
17365                 el.un('mouseleave' ,this.onEventLeave, this);
17366                 el.remove();
17367             },this);
17368         },this);
17369         
17370         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17371             e.remove();
17372         });
17373         
17374     },
17375     
17376     renderEvents: function()
17377     {   
17378         var _this = this;
17379         
17380         this.cells.each(function(c) {
17381             
17382             if(c.row < 5){
17383                 return;
17384             }
17385             
17386             var ev = c.events;
17387             
17388             var r = 4;
17389             if(c.row != c.events.length){
17390                 r = 4 - (4 - (c.row - c.events.length));
17391             }
17392             
17393             c.events = ev.slice(0, r);
17394             c.more = ev.slice(r);
17395             
17396             if(c.more.length && c.more.length == 1){
17397                 c.events.push(c.more.pop());
17398             }
17399             
17400             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17401             
17402         });
17403             
17404         this.cells.each(function(c) {
17405             
17406             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17407             
17408             
17409             for (var e = 0; e < c.events.length; e++){
17410                 var ev = c.events[e];
17411                 var rows = ev.rows;
17412                 
17413                 for(var i = 0; i < rows.length; i++) {
17414                 
17415                     // how many rows should it span..
17416
17417                     var  cfg = {
17418                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17419                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17420
17421                         unselectable : "on",
17422                         cn : [
17423                             {
17424                                 cls: 'fc-event-inner',
17425                                 cn : [
17426     //                                {
17427     //                                  tag:'span',
17428     //                                  cls: 'fc-event-time',
17429     //                                  html : cells.length > 1 ? '' : ev.time
17430     //                                },
17431                                     {
17432                                       tag:'span',
17433                                       cls: 'fc-event-title',
17434                                       html : String.format('{0}', ev.title)
17435                                     }
17436
17437
17438                                 ]
17439                             },
17440                             {
17441                                 cls: 'ui-resizable-handle ui-resizable-e',
17442                                 html : '&nbsp;&nbsp;&nbsp'
17443                             }
17444
17445                         ]
17446                     };
17447
17448                     if (i == 0) {
17449                         cfg.cls += ' fc-event-start';
17450                     }
17451                     if ((i+1) == rows.length) {
17452                         cfg.cls += ' fc-event-end';
17453                     }
17454
17455                     var ctr = _this.el.select('.fc-event-container',true).first();
17456                     var cg = ctr.createChild(cfg);
17457
17458                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17459                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17460
17461                     var r = (c.more.length) ? 1 : 0;
17462                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17463                     cg.setWidth(ebox.right - sbox.x -2);
17464
17465                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17466                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17467                     cg.on('click', _this.onEventClick, _this, ev);
17468
17469                     ev.els.push(cg);
17470                     
17471                 }
17472                 
17473             }
17474             
17475             
17476             if(c.more.length){
17477                 var  cfg = {
17478                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17479                     style : 'position: absolute',
17480                     unselectable : "on",
17481                     cn : [
17482                         {
17483                             cls: 'fc-event-inner',
17484                             cn : [
17485                                 {
17486                                   tag:'span',
17487                                   cls: 'fc-event-title',
17488                                   html : 'More'
17489                                 }
17490
17491
17492                             ]
17493                         },
17494                         {
17495                             cls: 'ui-resizable-handle ui-resizable-e',
17496                             html : '&nbsp;&nbsp;&nbsp'
17497                         }
17498
17499                     ]
17500                 };
17501
17502                 var ctr = _this.el.select('.fc-event-container',true).first();
17503                 var cg = ctr.createChild(cfg);
17504
17505                 var sbox = c.select('.fc-day-content',true).first().getBox();
17506                 var ebox = c.select('.fc-day-content',true).first().getBox();
17507                 //Roo.log(cg);
17508                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17509                 cg.setWidth(ebox.right - sbox.x -2);
17510
17511                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17512                 
17513             }
17514             
17515         });
17516         
17517         
17518         
17519     },
17520     
17521     onEventEnter: function (e, el,event,d) {
17522         this.fireEvent('evententer', this, el, event);
17523     },
17524     
17525     onEventLeave: function (e, el,event,d) {
17526         this.fireEvent('eventleave', this, el, event);
17527     },
17528     
17529     onEventClick: function (e, el,event,d) {
17530         this.fireEvent('eventclick', this, el, event);
17531     },
17532     
17533     onMonthChange: function () {
17534         this.store.load();
17535     },
17536     
17537     onMoreEventClick: function(e, el, more)
17538     {
17539         var _this = this;
17540         
17541         this.calpopover.placement = 'right';
17542         this.calpopover.setTitle('More');
17543         
17544         this.calpopover.setContent('');
17545         
17546         var ctr = this.calpopover.el.select('.popover-content', true).first();
17547         
17548         Roo.each(more, function(m){
17549             var cfg = {
17550                 cls : 'fc-event-hori fc-event-draggable',
17551                 html : m.title
17552             };
17553             var cg = ctr.createChild(cfg);
17554             
17555             cg.on('click', _this.onEventClick, _this, m);
17556         });
17557         
17558         this.calpopover.show(el);
17559         
17560         
17561     },
17562     
17563     onLoad: function () 
17564     {   
17565         this.calevents = [];
17566         var cal = this;
17567         
17568         if(this.store.getCount() > 0){
17569             this.store.data.each(function(d){
17570                cal.addItem({
17571                     id : d.data.id,
17572                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17573                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17574                     time : d.data.start_time,
17575                     title : d.data.title,
17576                     description : d.data.description,
17577                     venue : d.data.venue
17578                 });
17579             });
17580         }
17581         
17582         this.renderEvents();
17583         
17584         if(this.calevents.length && this.loadMask){
17585             this.maskEl.hide();
17586         }
17587     },
17588     
17589     onBeforeLoad: function()
17590     {
17591         this.clearEvents();
17592         if(this.loadMask){
17593             this.maskEl.show();
17594         }
17595     }
17596 });
17597
17598  
17599  /*
17600  * - LGPL
17601  *
17602  * element
17603  * 
17604  */
17605
17606 /**
17607  * @class Roo.bootstrap.Popover
17608  * @extends Roo.bootstrap.Component
17609  * Bootstrap Popover class
17610  * @cfg {String} html contents of the popover   (or false to use children..)
17611  * @cfg {String} title of popover (or false to hide)
17612  * @cfg {String} placement how it is placed
17613  * @cfg {String} trigger click || hover (or false to trigger manually)
17614  * @cfg {String} over what (parent or false to trigger manually.)
17615  * @cfg {Number} delay - delay before showing
17616  
17617  * @constructor
17618  * Create a new Popover
17619  * @param {Object} config The config object
17620  */
17621
17622 Roo.bootstrap.Popover = function(config){
17623     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17624     
17625     this.addEvents({
17626         // raw events
17627          /**
17628          * @event show
17629          * After the popover show
17630          * 
17631          * @param {Roo.bootstrap.Popover} this
17632          */
17633         "show" : true,
17634         /**
17635          * @event hide
17636          * After the popover hide
17637          * 
17638          * @param {Roo.bootstrap.Popover} this
17639          */
17640         "hide" : true
17641     });
17642 };
17643
17644 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17645     
17646     title: 'Fill in a title',
17647     html: false,
17648     
17649     placement : 'right',
17650     trigger : 'hover', // hover
17651     
17652     delay : 0,
17653     
17654     over: 'parent',
17655     
17656     can_build_overlaid : false,
17657     
17658     getChildContainer : function()
17659     {
17660         return this.el.select('.popover-content',true).first();
17661     },
17662     
17663     getAutoCreate : function(){
17664          
17665         var cfg = {
17666            cls : 'popover roo-dynamic',
17667            style: 'display:block',
17668            cn : [
17669                 {
17670                     cls : 'arrow'
17671                 },
17672                 {
17673                     cls : 'popover-inner',
17674                     cn : [
17675                         {
17676                             tag: 'h3',
17677                             cls: 'popover-title popover-header',
17678                             html : this.title
17679                         },
17680                         {
17681                             cls : 'popover-content popover-body',
17682                             html : this.html
17683                         }
17684                     ]
17685                     
17686                 }
17687            ]
17688         };
17689         
17690         return cfg;
17691     },
17692     setTitle: function(str)
17693     {
17694         this.title = str;
17695         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17696     },
17697     setContent: function(str)
17698     {
17699         this.html = str;
17700         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17701     },
17702     // as it get's added to the bottom of the page.
17703     onRender : function(ct, position)
17704     {
17705         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17706         if(!this.el){
17707             var cfg = Roo.apply({},  this.getAutoCreate());
17708             cfg.id = Roo.id();
17709             
17710             if (this.cls) {
17711                 cfg.cls += ' ' + this.cls;
17712             }
17713             if (this.style) {
17714                 cfg.style = this.style;
17715             }
17716             //Roo.log("adding to ");
17717             this.el = Roo.get(document.body).createChild(cfg, position);
17718 //            Roo.log(this.el);
17719         }
17720         this.initEvents();
17721     },
17722     
17723     initEvents : function()
17724     {
17725         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17726         this.el.enableDisplayMode('block');
17727         this.el.hide();
17728         if (this.over === false) {
17729             return; 
17730         }
17731         if (this.triggers === false) {
17732             return;
17733         }
17734         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17735         var triggers = this.trigger ? this.trigger.split(' ') : [];
17736         Roo.each(triggers, function(trigger) {
17737         
17738             if (trigger == 'click') {
17739                 on_el.on('click', this.toggle, this);
17740             } else if (trigger != 'manual') {
17741                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17742                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17743       
17744                 on_el.on(eventIn  ,this.enter, this);
17745                 on_el.on(eventOut, this.leave, this);
17746             }
17747         }, this);
17748         
17749     },
17750     
17751     
17752     // private
17753     timeout : null,
17754     hoverState : null,
17755     
17756     toggle : function () {
17757         this.hoverState == 'in' ? this.leave() : this.enter();
17758     },
17759     
17760     enter : function () {
17761         
17762         clearTimeout(this.timeout);
17763     
17764         this.hoverState = 'in';
17765     
17766         if (!this.delay || !this.delay.show) {
17767             this.show();
17768             return;
17769         }
17770         var _t = this;
17771         this.timeout = setTimeout(function () {
17772             if (_t.hoverState == 'in') {
17773                 _t.show();
17774             }
17775         }, this.delay.show)
17776     },
17777     
17778     leave : function() {
17779         clearTimeout(this.timeout);
17780     
17781         this.hoverState = 'out';
17782     
17783         if (!this.delay || !this.delay.hide) {
17784             this.hide();
17785             return;
17786         }
17787         var _t = this;
17788         this.timeout = setTimeout(function () {
17789             if (_t.hoverState == 'out') {
17790                 _t.hide();
17791             }
17792         }, this.delay.hide)
17793     },
17794     
17795     show : function (on_el)
17796     {
17797         if (!on_el) {
17798             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17799         }
17800         
17801         // set content.
17802         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17803         if (this.html !== false) {
17804             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17805         }
17806         this.el.removeClass([
17807             'fade','top','bottom', 'left', 'right','in',
17808             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17809         ]);
17810         if (!this.title.length) {
17811             this.el.select('.popover-title',true).hide();
17812         }
17813         
17814         var placement = typeof this.placement == 'function' ?
17815             this.placement.call(this, this.el, on_el) :
17816             this.placement;
17817             
17818         var autoToken = /\s?auto?\s?/i;
17819         var autoPlace = autoToken.test(placement);
17820         if (autoPlace) {
17821             placement = placement.replace(autoToken, '') || 'top';
17822         }
17823         
17824         //this.el.detach()
17825         //this.el.setXY([0,0]);
17826         this.el.show();
17827         this.el.dom.style.display='block';
17828         this.el.addClass(placement);
17829         
17830         //this.el.appendTo(on_el);
17831         
17832         var p = this.getPosition();
17833         var box = this.el.getBox();
17834         
17835         if (autoPlace) {
17836             // fixme..
17837         }
17838         var align = Roo.bootstrap.Popover.alignment[placement];
17839         
17840 //        Roo.log(align);
17841         this.el.alignTo(on_el, align[0],align[1]);
17842         //var arrow = this.el.select('.arrow',true).first();
17843         //arrow.set(align[2], 
17844         
17845         this.el.addClass('in');
17846         
17847         
17848         if (this.el.hasClass('fade')) {
17849             // fade it?
17850         }
17851         
17852         this.hoverState = 'in';
17853         
17854         this.fireEvent('show', this);
17855         
17856     },
17857     hide : function()
17858     {
17859         this.el.setXY([0,0]);
17860         this.el.removeClass('in');
17861         this.el.hide();
17862         this.hoverState = null;
17863         
17864         this.fireEvent('hide', this);
17865     }
17866     
17867 });
17868
17869 Roo.bootstrap.Popover.alignment = {
17870     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
17871     'right' : ['l-r', [10,0], 'left bs-popover-left'],
17872     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
17873     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
17874 };
17875
17876  /*
17877  * - LGPL
17878  *
17879  * Progress
17880  * 
17881  */
17882
17883 /**
17884  * @class Roo.bootstrap.Progress
17885  * @extends Roo.bootstrap.Component
17886  * Bootstrap Progress class
17887  * @cfg {Boolean} striped striped of the progress bar
17888  * @cfg {Boolean} active animated of the progress bar
17889  * 
17890  * 
17891  * @constructor
17892  * Create a new Progress
17893  * @param {Object} config The config object
17894  */
17895
17896 Roo.bootstrap.Progress = function(config){
17897     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17898 };
17899
17900 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17901     
17902     striped : false,
17903     active: false,
17904     
17905     getAutoCreate : function(){
17906         var cfg = {
17907             tag: 'div',
17908             cls: 'progress'
17909         };
17910         
17911         
17912         if(this.striped){
17913             cfg.cls += ' progress-striped';
17914         }
17915       
17916         if(this.active){
17917             cfg.cls += ' active';
17918         }
17919         
17920         
17921         return cfg;
17922     }
17923    
17924 });
17925
17926  
17927
17928  /*
17929  * - LGPL
17930  *
17931  * ProgressBar
17932  * 
17933  */
17934
17935 /**
17936  * @class Roo.bootstrap.ProgressBar
17937  * @extends Roo.bootstrap.Component
17938  * Bootstrap ProgressBar class
17939  * @cfg {Number} aria_valuenow aria-value now
17940  * @cfg {Number} aria_valuemin aria-value min
17941  * @cfg {Number} aria_valuemax aria-value max
17942  * @cfg {String} label label for the progress bar
17943  * @cfg {String} panel (success | info | warning | danger )
17944  * @cfg {String} role role of the progress bar
17945  * @cfg {String} sr_only text
17946  * 
17947  * 
17948  * @constructor
17949  * Create a new ProgressBar
17950  * @param {Object} config The config object
17951  */
17952
17953 Roo.bootstrap.ProgressBar = function(config){
17954     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17955 };
17956
17957 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17958     
17959     aria_valuenow : 0,
17960     aria_valuemin : 0,
17961     aria_valuemax : 100,
17962     label : false,
17963     panel : false,
17964     role : false,
17965     sr_only: false,
17966     
17967     getAutoCreate : function()
17968     {
17969         
17970         var cfg = {
17971             tag: 'div',
17972             cls: 'progress-bar',
17973             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17974         };
17975         
17976         if(this.sr_only){
17977             cfg.cn = {
17978                 tag: 'span',
17979                 cls: 'sr-only',
17980                 html: this.sr_only
17981             }
17982         }
17983         
17984         if(this.role){
17985             cfg.role = this.role;
17986         }
17987         
17988         if(this.aria_valuenow){
17989             cfg['aria-valuenow'] = this.aria_valuenow;
17990         }
17991         
17992         if(this.aria_valuemin){
17993             cfg['aria-valuemin'] = this.aria_valuemin;
17994         }
17995         
17996         if(this.aria_valuemax){
17997             cfg['aria-valuemax'] = this.aria_valuemax;
17998         }
17999         
18000         if(this.label && !this.sr_only){
18001             cfg.html = this.label;
18002         }
18003         
18004         if(this.panel){
18005             cfg.cls += ' progress-bar-' + this.panel;
18006         }
18007         
18008         return cfg;
18009     },
18010     
18011     update : function(aria_valuenow)
18012     {
18013         this.aria_valuenow = aria_valuenow;
18014         
18015         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18016     }
18017    
18018 });
18019
18020  
18021
18022  /*
18023  * - LGPL
18024  *
18025  * column
18026  * 
18027  */
18028
18029 /**
18030  * @class Roo.bootstrap.TabGroup
18031  * @extends Roo.bootstrap.Column
18032  * Bootstrap Column class
18033  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18034  * @cfg {Boolean} carousel true to make the group behave like a carousel
18035  * @cfg {Boolean} bullets show bullets for the panels
18036  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18037  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18038  * @cfg {Boolean} showarrow (true|false) show arrow default true
18039  * 
18040  * @constructor
18041  * Create a new TabGroup
18042  * @param {Object} config The config object
18043  */
18044
18045 Roo.bootstrap.TabGroup = function(config){
18046     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18047     if (!this.navId) {
18048         this.navId = Roo.id();
18049     }
18050     this.tabs = [];
18051     Roo.bootstrap.TabGroup.register(this);
18052     
18053 };
18054
18055 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18056     
18057     carousel : false,
18058     transition : false,
18059     bullets : 0,
18060     timer : 0,
18061     autoslide : false,
18062     slideFn : false,
18063     slideOnTouch : false,
18064     showarrow : true,
18065     
18066     getAutoCreate : function()
18067     {
18068         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18069         
18070         cfg.cls += ' tab-content';
18071         
18072         if (this.carousel) {
18073             cfg.cls += ' carousel slide';
18074             
18075             cfg.cn = [{
18076                cls : 'carousel-inner',
18077                cn : []
18078             }];
18079         
18080             if(this.bullets  && !Roo.isTouch){
18081                 
18082                 var bullets = {
18083                     cls : 'carousel-bullets',
18084                     cn : []
18085                 };
18086                
18087                 if(this.bullets_cls){
18088                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18089                 }
18090                 
18091                 bullets.cn.push({
18092                     cls : 'clear'
18093                 });
18094                 
18095                 cfg.cn[0].cn.push(bullets);
18096             }
18097             
18098             if(this.showarrow){
18099                 cfg.cn[0].cn.push({
18100                     tag : 'div',
18101                     class : 'carousel-arrow',
18102                     cn : [
18103                         {
18104                             tag : 'div',
18105                             class : 'carousel-prev',
18106                             cn : [
18107                                 {
18108                                     tag : 'i',
18109                                     class : 'fa fa-chevron-left'
18110                                 }
18111                             ]
18112                         },
18113                         {
18114                             tag : 'div',
18115                             class : 'carousel-next',
18116                             cn : [
18117                                 {
18118                                     tag : 'i',
18119                                     class : 'fa fa-chevron-right'
18120                                 }
18121                             ]
18122                         }
18123                     ]
18124                 });
18125             }
18126             
18127         }
18128         
18129         return cfg;
18130     },
18131     
18132     initEvents:  function()
18133     {
18134 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18135 //            this.el.on("touchstart", this.onTouchStart, this);
18136 //        }
18137         
18138         if(this.autoslide){
18139             var _this = this;
18140             
18141             this.slideFn = window.setInterval(function() {
18142                 _this.showPanelNext();
18143             }, this.timer);
18144         }
18145         
18146         if(this.showarrow){
18147             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18148             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18149         }
18150         
18151         
18152     },
18153     
18154 //    onTouchStart : function(e, el, o)
18155 //    {
18156 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18157 //            return;
18158 //        }
18159 //        
18160 //        this.showPanelNext();
18161 //    },
18162     
18163     
18164     getChildContainer : function()
18165     {
18166         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18167     },
18168     
18169     /**
18170     * register a Navigation item
18171     * @param {Roo.bootstrap.NavItem} the navitem to add
18172     */
18173     register : function(item)
18174     {
18175         this.tabs.push( item);
18176         item.navId = this.navId; // not really needed..
18177         this.addBullet();
18178     
18179     },
18180     
18181     getActivePanel : function()
18182     {
18183         var r = false;
18184         Roo.each(this.tabs, function(t) {
18185             if (t.active) {
18186                 r = t;
18187                 return false;
18188             }
18189             return null;
18190         });
18191         return r;
18192         
18193     },
18194     getPanelByName : function(n)
18195     {
18196         var r = false;
18197         Roo.each(this.tabs, function(t) {
18198             if (t.tabId == n) {
18199                 r = t;
18200                 return false;
18201             }
18202             return null;
18203         });
18204         return r;
18205     },
18206     indexOfPanel : function(p)
18207     {
18208         var r = false;
18209         Roo.each(this.tabs, function(t,i) {
18210             if (t.tabId == p.tabId) {
18211                 r = i;
18212                 return false;
18213             }
18214             return null;
18215         });
18216         return r;
18217     },
18218     /**
18219      * show a specific panel
18220      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18221      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18222      */
18223     showPanel : function (pan)
18224     {
18225         if(this.transition || typeof(pan) == 'undefined'){
18226             Roo.log("waiting for the transitionend");
18227             return;
18228         }
18229         
18230         if (typeof(pan) == 'number') {
18231             pan = this.tabs[pan];
18232         }
18233         
18234         if (typeof(pan) == 'string') {
18235             pan = this.getPanelByName(pan);
18236         }
18237         
18238         var cur = this.getActivePanel();
18239         
18240         if(!pan || !cur){
18241             Roo.log('pan or acitve pan is undefined');
18242             return false;
18243         }
18244         
18245         if (pan.tabId == this.getActivePanel().tabId) {
18246             return true;
18247         }
18248         
18249         if (false === cur.fireEvent('beforedeactivate')) {
18250             return false;
18251         }
18252         
18253         if(this.bullets > 0 && !Roo.isTouch){
18254             this.setActiveBullet(this.indexOfPanel(pan));
18255         }
18256         
18257         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18258             
18259             this.transition = true;
18260             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18261             var lr = dir == 'next' ? 'left' : 'right';
18262             pan.el.addClass(dir); // or prev
18263             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18264             cur.el.addClass(lr); // or right
18265             pan.el.addClass(lr);
18266             
18267             var _this = this;
18268             cur.el.on('transitionend', function() {
18269                 Roo.log("trans end?");
18270                 
18271                 pan.el.removeClass([lr,dir]);
18272                 pan.setActive(true);
18273                 
18274                 cur.el.removeClass([lr]);
18275                 cur.setActive(false);
18276                 
18277                 _this.transition = false;
18278                 
18279             }, this, { single:  true } );
18280             
18281             return true;
18282         }
18283         
18284         cur.setActive(false);
18285         pan.setActive(true);
18286         
18287         return true;
18288         
18289     },
18290     showPanelNext : function()
18291     {
18292         var i = this.indexOfPanel(this.getActivePanel());
18293         
18294         if (i >= this.tabs.length - 1 && !this.autoslide) {
18295             return;
18296         }
18297         
18298         if (i >= this.tabs.length - 1 && this.autoslide) {
18299             i = -1;
18300         }
18301         
18302         this.showPanel(this.tabs[i+1]);
18303     },
18304     
18305     showPanelPrev : function()
18306     {
18307         var i = this.indexOfPanel(this.getActivePanel());
18308         
18309         if (i  < 1 && !this.autoslide) {
18310             return;
18311         }
18312         
18313         if (i < 1 && this.autoslide) {
18314             i = this.tabs.length;
18315         }
18316         
18317         this.showPanel(this.tabs[i-1]);
18318     },
18319     
18320     
18321     addBullet: function()
18322     {
18323         if(!this.bullets || Roo.isTouch){
18324             return;
18325         }
18326         var ctr = this.el.select('.carousel-bullets',true).first();
18327         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18328         var bullet = ctr.createChild({
18329             cls : 'bullet bullet-' + i
18330         },ctr.dom.lastChild);
18331         
18332         
18333         var _this = this;
18334         
18335         bullet.on('click', (function(e, el, o, ii, t){
18336
18337             e.preventDefault();
18338
18339             this.showPanel(ii);
18340
18341             if(this.autoslide && this.slideFn){
18342                 clearInterval(this.slideFn);
18343                 this.slideFn = window.setInterval(function() {
18344                     _this.showPanelNext();
18345                 }, this.timer);
18346             }
18347
18348         }).createDelegate(this, [i, bullet], true));
18349                 
18350         
18351     },
18352      
18353     setActiveBullet : function(i)
18354     {
18355         if(Roo.isTouch){
18356             return;
18357         }
18358         
18359         Roo.each(this.el.select('.bullet', true).elements, function(el){
18360             el.removeClass('selected');
18361         });
18362
18363         var bullet = this.el.select('.bullet-' + i, true).first();
18364         
18365         if(!bullet){
18366             return;
18367         }
18368         
18369         bullet.addClass('selected');
18370     }
18371     
18372     
18373   
18374 });
18375
18376  
18377
18378  
18379  
18380 Roo.apply(Roo.bootstrap.TabGroup, {
18381     
18382     groups: {},
18383      /**
18384     * register a Navigation Group
18385     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18386     */
18387     register : function(navgrp)
18388     {
18389         this.groups[navgrp.navId] = navgrp;
18390         
18391     },
18392     /**
18393     * fetch a Navigation Group based on the navigation ID
18394     * if one does not exist , it will get created.
18395     * @param {string} the navgroup to add
18396     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18397     */
18398     get: function(navId) {
18399         if (typeof(this.groups[navId]) == 'undefined') {
18400             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18401         }
18402         return this.groups[navId] ;
18403     }
18404     
18405     
18406     
18407 });
18408
18409  /*
18410  * - LGPL
18411  *
18412  * TabPanel
18413  * 
18414  */
18415
18416 /**
18417  * @class Roo.bootstrap.TabPanel
18418  * @extends Roo.bootstrap.Component
18419  * Bootstrap TabPanel class
18420  * @cfg {Boolean} active panel active
18421  * @cfg {String} html panel content
18422  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18423  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18424  * @cfg {String} href click to link..
18425  * 
18426  * 
18427  * @constructor
18428  * Create a new TabPanel
18429  * @param {Object} config The config object
18430  */
18431
18432 Roo.bootstrap.TabPanel = function(config){
18433     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18434     this.addEvents({
18435         /**
18436              * @event changed
18437              * Fires when the active status changes
18438              * @param {Roo.bootstrap.TabPanel} this
18439              * @param {Boolean} state the new state
18440             
18441          */
18442         'changed': true,
18443         /**
18444              * @event beforedeactivate
18445              * Fires before a tab is de-activated - can be used to do validation on a form.
18446              * @param {Roo.bootstrap.TabPanel} this
18447              * @return {Boolean} false if there is an error
18448             
18449          */
18450         'beforedeactivate': true
18451      });
18452     
18453     this.tabId = this.tabId || Roo.id();
18454   
18455 };
18456
18457 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18458     
18459     active: false,
18460     html: false,
18461     tabId: false,
18462     navId : false,
18463     href : '',
18464     
18465     getAutoCreate : function(){
18466         var cfg = {
18467             tag: 'div',
18468             // item is needed for carousel - not sure if it has any effect otherwise
18469             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18470             html: this.html || ''
18471         };
18472         
18473         if(this.active){
18474             cfg.cls += ' active';
18475         }
18476         
18477         if(this.tabId){
18478             cfg.tabId = this.tabId;
18479         }
18480         
18481         
18482         return cfg;
18483     },
18484     
18485     initEvents:  function()
18486     {
18487         var p = this.parent();
18488         
18489         this.navId = this.navId || p.navId;
18490         
18491         if (typeof(this.navId) != 'undefined') {
18492             // not really needed.. but just in case.. parent should be a NavGroup.
18493             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18494             
18495             tg.register(this);
18496             
18497             var i = tg.tabs.length - 1;
18498             
18499             if(this.active && tg.bullets > 0 && i < tg.bullets){
18500                 tg.setActiveBullet(i);
18501             }
18502         }
18503         
18504         this.el.on('click', this.onClick, this);
18505         
18506         if(Roo.isTouch){
18507             this.el.on("touchstart", this.onTouchStart, this);
18508             this.el.on("touchmove", this.onTouchMove, this);
18509             this.el.on("touchend", this.onTouchEnd, this);
18510         }
18511         
18512     },
18513     
18514     onRender : function(ct, position)
18515     {
18516         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18517     },
18518     
18519     setActive : function(state)
18520     {
18521         Roo.log("panel - set active " + this.tabId + "=" + state);
18522         
18523         this.active = state;
18524         if (!state) {
18525             this.el.removeClass('active');
18526             
18527         } else  if (!this.el.hasClass('active')) {
18528             this.el.addClass('active');
18529         }
18530         
18531         this.fireEvent('changed', this, state);
18532     },
18533     
18534     onClick : function(e)
18535     {
18536         e.preventDefault();
18537         
18538         if(!this.href.length){
18539             return;
18540         }
18541         
18542         window.location.href = this.href;
18543     },
18544     
18545     startX : 0,
18546     startY : 0,
18547     endX : 0,
18548     endY : 0,
18549     swiping : false,
18550     
18551     onTouchStart : function(e)
18552     {
18553         this.swiping = false;
18554         
18555         this.startX = e.browserEvent.touches[0].clientX;
18556         this.startY = e.browserEvent.touches[0].clientY;
18557     },
18558     
18559     onTouchMove : function(e)
18560     {
18561         this.swiping = true;
18562         
18563         this.endX = e.browserEvent.touches[0].clientX;
18564         this.endY = e.browserEvent.touches[0].clientY;
18565     },
18566     
18567     onTouchEnd : function(e)
18568     {
18569         if(!this.swiping){
18570             this.onClick(e);
18571             return;
18572         }
18573         
18574         var tabGroup = this.parent();
18575         
18576         if(this.endX > this.startX){ // swiping right
18577             tabGroup.showPanelPrev();
18578             return;
18579         }
18580         
18581         if(this.startX > this.endX){ // swiping left
18582             tabGroup.showPanelNext();
18583             return;
18584         }
18585     }
18586     
18587     
18588 });
18589  
18590
18591  
18592
18593  /*
18594  * - LGPL
18595  *
18596  * DateField
18597  * 
18598  */
18599
18600 /**
18601  * @class Roo.bootstrap.DateField
18602  * @extends Roo.bootstrap.Input
18603  * Bootstrap DateField class
18604  * @cfg {Number} weekStart default 0
18605  * @cfg {String} viewMode default empty, (months|years)
18606  * @cfg {String} minViewMode default empty, (months|years)
18607  * @cfg {Number} startDate default -Infinity
18608  * @cfg {Number} endDate default Infinity
18609  * @cfg {Boolean} todayHighlight default false
18610  * @cfg {Boolean} todayBtn default false
18611  * @cfg {Boolean} calendarWeeks default false
18612  * @cfg {Object} daysOfWeekDisabled default empty
18613  * @cfg {Boolean} singleMode default false (true | false)
18614  * 
18615  * @cfg {Boolean} keyboardNavigation default true
18616  * @cfg {String} language default en
18617  * 
18618  * @constructor
18619  * Create a new DateField
18620  * @param {Object} config The config object
18621  */
18622
18623 Roo.bootstrap.DateField = function(config){
18624     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18625      this.addEvents({
18626             /**
18627              * @event show
18628              * Fires when this field show.
18629              * @param {Roo.bootstrap.DateField} this
18630              * @param {Mixed} date The date value
18631              */
18632             show : true,
18633             /**
18634              * @event show
18635              * Fires when this field hide.
18636              * @param {Roo.bootstrap.DateField} this
18637              * @param {Mixed} date The date value
18638              */
18639             hide : true,
18640             /**
18641              * @event select
18642              * Fires when select a date.
18643              * @param {Roo.bootstrap.DateField} this
18644              * @param {Mixed} date The date value
18645              */
18646             select : true,
18647             /**
18648              * @event beforeselect
18649              * Fires when before select a date.
18650              * @param {Roo.bootstrap.DateField} this
18651              * @param {Mixed} date The date value
18652              */
18653             beforeselect : true
18654         });
18655 };
18656
18657 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18658     
18659     /**
18660      * @cfg {String} format
18661      * The default date format string which can be overriden for localization support.  The format must be
18662      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18663      */
18664     format : "m/d/y",
18665     /**
18666      * @cfg {String} altFormats
18667      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18668      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18669      */
18670     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18671     
18672     weekStart : 0,
18673     
18674     viewMode : '',
18675     
18676     minViewMode : '',
18677     
18678     todayHighlight : false,
18679     
18680     todayBtn: false,
18681     
18682     language: 'en',
18683     
18684     keyboardNavigation: true,
18685     
18686     calendarWeeks: false,
18687     
18688     startDate: -Infinity,
18689     
18690     endDate: Infinity,
18691     
18692     daysOfWeekDisabled: [],
18693     
18694     _events: [],
18695     
18696     singleMode : false,
18697     
18698     UTCDate: function()
18699     {
18700         return new Date(Date.UTC.apply(Date, arguments));
18701     },
18702     
18703     UTCToday: function()
18704     {
18705         var today = new Date();
18706         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18707     },
18708     
18709     getDate: function() {
18710             var d = this.getUTCDate();
18711             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18712     },
18713     
18714     getUTCDate: function() {
18715             return this.date;
18716     },
18717     
18718     setDate: function(d) {
18719             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18720     },
18721     
18722     setUTCDate: function(d) {
18723             this.date = d;
18724             this.setValue(this.formatDate(this.date));
18725     },
18726         
18727     onRender: function(ct, position)
18728     {
18729         
18730         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18731         
18732         this.language = this.language || 'en';
18733         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18734         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18735         
18736         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18737         this.format = this.format || 'm/d/y';
18738         this.isInline = false;
18739         this.isInput = true;
18740         this.component = this.el.select('.add-on', true).first() || false;
18741         this.component = (this.component && this.component.length === 0) ? false : this.component;
18742         this.hasInput = this.component && this.inputEl().length;
18743         
18744         if (typeof(this.minViewMode === 'string')) {
18745             switch (this.minViewMode) {
18746                 case 'months':
18747                     this.minViewMode = 1;
18748                     break;
18749                 case 'years':
18750                     this.minViewMode = 2;
18751                     break;
18752                 default:
18753                     this.minViewMode = 0;
18754                     break;
18755             }
18756         }
18757         
18758         if (typeof(this.viewMode === 'string')) {
18759             switch (this.viewMode) {
18760                 case 'months':
18761                     this.viewMode = 1;
18762                     break;
18763                 case 'years':
18764                     this.viewMode = 2;
18765                     break;
18766                 default:
18767                     this.viewMode = 0;
18768                     break;
18769             }
18770         }
18771                 
18772         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18773         
18774 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18775         
18776         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18777         
18778         this.picker().on('mousedown', this.onMousedown, this);
18779         this.picker().on('click', this.onClick, this);
18780         
18781         this.picker().addClass('datepicker-dropdown');
18782         
18783         this.startViewMode = this.viewMode;
18784         
18785         if(this.singleMode){
18786             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18787                 v.setVisibilityMode(Roo.Element.DISPLAY);
18788                 v.hide();
18789             });
18790             
18791             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18792                 v.setStyle('width', '189px');
18793             });
18794         }
18795         
18796         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18797             if(!this.calendarWeeks){
18798                 v.remove();
18799                 return;
18800             }
18801             
18802             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18803             v.attr('colspan', function(i, val){
18804                 return parseInt(val) + 1;
18805             });
18806         });
18807                         
18808         
18809         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18810         
18811         this.setStartDate(this.startDate);
18812         this.setEndDate(this.endDate);
18813         
18814         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18815         
18816         this.fillDow();
18817         this.fillMonths();
18818         this.update();
18819         this.showMode();
18820         
18821         if(this.isInline) {
18822             this.showPopup();
18823         }
18824     },
18825     
18826     picker : function()
18827     {
18828         return this.pickerEl;
18829 //        return this.el.select('.datepicker', true).first();
18830     },
18831     
18832     fillDow: function()
18833     {
18834         var dowCnt = this.weekStart;
18835         
18836         var dow = {
18837             tag: 'tr',
18838             cn: [
18839                 
18840             ]
18841         };
18842         
18843         if(this.calendarWeeks){
18844             dow.cn.push({
18845                 tag: 'th',
18846                 cls: 'cw',
18847                 html: '&nbsp;'
18848             })
18849         }
18850         
18851         while (dowCnt < this.weekStart + 7) {
18852             dow.cn.push({
18853                 tag: 'th',
18854                 cls: 'dow',
18855                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18856             });
18857         }
18858         
18859         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18860     },
18861     
18862     fillMonths: function()
18863     {    
18864         var i = 0;
18865         var months = this.picker().select('>.datepicker-months td', true).first();
18866         
18867         months.dom.innerHTML = '';
18868         
18869         while (i < 12) {
18870             var month = {
18871                 tag: 'span',
18872                 cls: 'month',
18873                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18874             };
18875             
18876             months.createChild(month);
18877         }
18878         
18879     },
18880     
18881     update: function()
18882     {
18883         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;
18884         
18885         if (this.date < this.startDate) {
18886             this.viewDate = new Date(this.startDate);
18887         } else if (this.date > this.endDate) {
18888             this.viewDate = new Date(this.endDate);
18889         } else {
18890             this.viewDate = new Date(this.date);
18891         }
18892         
18893         this.fill();
18894     },
18895     
18896     fill: function() 
18897     {
18898         var d = new Date(this.viewDate),
18899                 year = d.getUTCFullYear(),
18900                 month = d.getUTCMonth(),
18901                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18902                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18903                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18904                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18905                 currentDate = this.date && this.date.valueOf(),
18906                 today = this.UTCToday();
18907         
18908         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18909         
18910 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18911         
18912 //        this.picker.select('>tfoot th.today').
18913 //                                              .text(dates[this.language].today)
18914 //                                              .toggle(this.todayBtn !== false);
18915     
18916         this.updateNavArrows();
18917         this.fillMonths();
18918                                                 
18919         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18920         
18921         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18922          
18923         prevMonth.setUTCDate(day);
18924         
18925         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18926         
18927         var nextMonth = new Date(prevMonth);
18928         
18929         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18930         
18931         nextMonth = nextMonth.valueOf();
18932         
18933         var fillMonths = false;
18934         
18935         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18936         
18937         while(prevMonth.valueOf() <= nextMonth) {
18938             var clsName = '';
18939             
18940             if (prevMonth.getUTCDay() === this.weekStart) {
18941                 if(fillMonths){
18942                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18943                 }
18944                     
18945                 fillMonths = {
18946                     tag: 'tr',
18947                     cn: []
18948                 };
18949                 
18950                 if(this.calendarWeeks){
18951                     // ISO 8601: First week contains first thursday.
18952                     // ISO also states week starts on Monday, but we can be more abstract here.
18953                     var
18954                     // Start of current week: based on weekstart/current date
18955                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18956                     // Thursday of this week
18957                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18958                     // First Thursday of year, year from thursday
18959                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18960                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18961                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18962                     
18963                     fillMonths.cn.push({
18964                         tag: 'td',
18965                         cls: 'cw',
18966                         html: calWeek
18967                     });
18968                 }
18969             }
18970             
18971             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18972                 clsName += ' old';
18973             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18974                 clsName += ' new';
18975             }
18976             if (this.todayHighlight &&
18977                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18978                 prevMonth.getUTCMonth() == today.getMonth() &&
18979                 prevMonth.getUTCDate() == today.getDate()) {
18980                 clsName += ' today';
18981             }
18982             
18983             if (currentDate && prevMonth.valueOf() === currentDate) {
18984                 clsName += ' active';
18985             }
18986             
18987             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18988                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18989                     clsName += ' disabled';
18990             }
18991             
18992             fillMonths.cn.push({
18993                 tag: 'td',
18994                 cls: 'day ' + clsName,
18995                 html: prevMonth.getDate()
18996             });
18997             
18998             prevMonth.setDate(prevMonth.getDate()+1);
18999         }
19000           
19001         var currentYear = this.date && this.date.getUTCFullYear();
19002         var currentMonth = this.date && this.date.getUTCMonth();
19003         
19004         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19005         
19006         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19007             v.removeClass('active');
19008             
19009             if(currentYear === year && k === currentMonth){
19010                 v.addClass('active');
19011             }
19012             
19013             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19014                 v.addClass('disabled');
19015             }
19016             
19017         });
19018         
19019         
19020         year = parseInt(year/10, 10) * 10;
19021         
19022         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19023         
19024         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19025         
19026         year -= 1;
19027         for (var i = -1; i < 11; i++) {
19028             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19029                 tag: 'span',
19030                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19031                 html: year
19032             });
19033             
19034             year += 1;
19035         }
19036     },
19037     
19038     showMode: function(dir) 
19039     {
19040         if (dir) {
19041             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19042         }
19043         
19044         Roo.each(this.picker().select('>div',true).elements, function(v){
19045             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19046             v.hide();
19047         });
19048         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19049     },
19050     
19051     place: function()
19052     {
19053         if(this.isInline) {
19054             return;
19055         }
19056         
19057         this.picker().removeClass(['bottom', 'top']);
19058         
19059         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19060             /*
19061              * place to the top of element!
19062              *
19063              */
19064             
19065             this.picker().addClass('top');
19066             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19067             
19068             return;
19069         }
19070         
19071         this.picker().addClass('bottom');
19072         
19073         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19074     },
19075     
19076     parseDate : function(value)
19077     {
19078         if(!value || value instanceof Date){
19079             return value;
19080         }
19081         var v = Date.parseDate(value, this.format);
19082         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19083             v = Date.parseDate(value, 'Y-m-d');
19084         }
19085         if(!v && this.altFormats){
19086             if(!this.altFormatsArray){
19087                 this.altFormatsArray = this.altFormats.split("|");
19088             }
19089             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19090                 v = Date.parseDate(value, this.altFormatsArray[i]);
19091             }
19092         }
19093         return v;
19094     },
19095     
19096     formatDate : function(date, fmt)
19097     {   
19098         return (!date || !(date instanceof Date)) ?
19099         date : date.dateFormat(fmt || this.format);
19100     },
19101     
19102     onFocus : function()
19103     {
19104         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19105         this.showPopup();
19106     },
19107     
19108     onBlur : function()
19109     {
19110         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19111         
19112         var d = this.inputEl().getValue();
19113         
19114         this.setValue(d);
19115                 
19116         this.hidePopup();
19117     },
19118     
19119     showPopup : function()
19120     {
19121         this.picker().show();
19122         this.update();
19123         this.place();
19124         
19125         this.fireEvent('showpopup', this, this.date);
19126     },
19127     
19128     hidePopup : function()
19129     {
19130         if(this.isInline) {
19131             return;
19132         }
19133         this.picker().hide();
19134         this.viewMode = this.startViewMode;
19135         this.showMode();
19136         
19137         this.fireEvent('hidepopup', this, this.date);
19138         
19139     },
19140     
19141     onMousedown: function(e)
19142     {
19143         e.stopPropagation();
19144         e.preventDefault();
19145     },
19146     
19147     keyup: function(e)
19148     {
19149         Roo.bootstrap.DateField.superclass.keyup.call(this);
19150         this.update();
19151     },
19152
19153     setValue: function(v)
19154     {
19155         if(this.fireEvent('beforeselect', this, v) !== false){
19156             var d = new Date(this.parseDate(v) ).clearTime();
19157         
19158             if(isNaN(d.getTime())){
19159                 this.date = this.viewDate = '';
19160                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19161                 return;
19162             }
19163
19164             v = this.formatDate(d);
19165
19166             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19167
19168             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19169
19170             this.update();
19171
19172             this.fireEvent('select', this, this.date);
19173         }
19174     },
19175     
19176     getValue: function()
19177     {
19178         return this.formatDate(this.date);
19179     },
19180     
19181     fireKey: function(e)
19182     {
19183         if (!this.picker().isVisible()){
19184             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19185                 this.showPopup();
19186             }
19187             return;
19188         }
19189         
19190         var dateChanged = false,
19191         dir, day, month,
19192         newDate, newViewDate;
19193         
19194         switch(e.keyCode){
19195             case 27: // escape
19196                 this.hidePopup();
19197                 e.preventDefault();
19198                 break;
19199             case 37: // left
19200             case 39: // right
19201                 if (!this.keyboardNavigation) {
19202                     break;
19203                 }
19204                 dir = e.keyCode == 37 ? -1 : 1;
19205                 
19206                 if (e.ctrlKey){
19207                     newDate = this.moveYear(this.date, dir);
19208                     newViewDate = this.moveYear(this.viewDate, dir);
19209                 } else if (e.shiftKey){
19210                     newDate = this.moveMonth(this.date, dir);
19211                     newViewDate = this.moveMonth(this.viewDate, dir);
19212                 } else {
19213                     newDate = new Date(this.date);
19214                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19215                     newViewDate = new Date(this.viewDate);
19216                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19217                 }
19218                 if (this.dateWithinRange(newDate)){
19219                     this.date = newDate;
19220                     this.viewDate = newViewDate;
19221                     this.setValue(this.formatDate(this.date));
19222 //                    this.update();
19223                     e.preventDefault();
19224                     dateChanged = true;
19225                 }
19226                 break;
19227             case 38: // up
19228             case 40: // down
19229                 if (!this.keyboardNavigation) {
19230                     break;
19231                 }
19232                 dir = e.keyCode == 38 ? -1 : 1;
19233                 if (e.ctrlKey){
19234                     newDate = this.moveYear(this.date, dir);
19235                     newViewDate = this.moveYear(this.viewDate, dir);
19236                 } else if (e.shiftKey){
19237                     newDate = this.moveMonth(this.date, dir);
19238                     newViewDate = this.moveMonth(this.viewDate, dir);
19239                 } else {
19240                     newDate = new Date(this.date);
19241                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19242                     newViewDate = new Date(this.viewDate);
19243                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19244                 }
19245                 if (this.dateWithinRange(newDate)){
19246                     this.date = newDate;
19247                     this.viewDate = newViewDate;
19248                     this.setValue(this.formatDate(this.date));
19249 //                    this.update();
19250                     e.preventDefault();
19251                     dateChanged = true;
19252                 }
19253                 break;
19254             case 13: // enter
19255                 this.setValue(this.formatDate(this.date));
19256                 this.hidePopup();
19257                 e.preventDefault();
19258                 break;
19259             case 9: // tab
19260                 this.setValue(this.formatDate(this.date));
19261                 this.hidePopup();
19262                 break;
19263             case 16: // shift
19264             case 17: // ctrl
19265             case 18: // alt
19266                 break;
19267             default :
19268                 this.hidePopup();
19269                 
19270         }
19271     },
19272     
19273     
19274     onClick: function(e) 
19275     {
19276         e.stopPropagation();
19277         e.preventDefault();
19278         
19279         var target = e.getTarget();
19280         
19281         if(target.nodeName.toLowerCase() === 'i'){
19282             target = Roo.get(target).dom.parentNode;
19283         }
19284         
19285         var nodeName = target.nodeName;
19286         var className = target.className;
19287         var html = target.innerHTML;
19288         //Roo.log(nodeName);
19289         
19290         switch(nodeName.toLowerCase()) {
19291             case 'th':
19292                 switch(className) {
19293                     case 'switch':
19294                         this.showMode(1);
19295                         break;
19296                     case 'prev':
19297                     case 'next':
19298                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19299                         switch(this.viewMode){
19300                                 case 0:
19301                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19302                                         break;
19303                                 case 1:
19304                                 case 2:
19305                                         this.viewDate = this.moveYear(this.viewDate, dir);
19306                                         break;
19307                         }
19308                         this.fill();
19309                         break;
19310                     case 'today':
19311                         var date = new Date();
19312                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19313 //                        this.fill()
19314                         this.setValue(this.formatDate(this.date));
19315                         
19316                         this.hidePopup();
19317                         break;
19318                 }
19319                 break;
19320             case 'span':
19321                 if (className.indexOf('disabled') < 0) {
19322                     this.viewDate.setUTCDate(1);
19323                     if (className.indexOf('month') > -1) {
19324                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19325                     } else {
19326                         var year = parseInt(html, 10) || 0;
19327                         this.viewDate.setUTCFullYear(year);
19328                         
19329                     }
19330                     
19331                     if(this.singleMode){
19332                         this.setValue(this.formatDate(this.viewDate));
19333                         this.hidePopup();
19334                         return;
19335                     }
19336                     
19337                     this.showMode(-1);
19338                     this.fill();
19339                 }
19340                 break;
19341                 
19342             case 'td':
19343                 //Roo.log(className);
19344                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19345                     var day = parseInt(html, 10) || 1;
19346                     var year = this.viewDate.getUTCFullYear(),
19347                         month = this.viewDate.getUTCMonth();
19348
19349                     if (className.indexOf('old') > -1) {
19350                         if(month === 0 ){
19351                             month = 11;
19352                             year -= 1;
19353                         }else{
19354                             month -= 1;
19355                         }
19356                     } else if (className.indexOf('new') > -1) {
19357                         if (month == 11) {
19358                             month = 0;
19359                             year += 1;
19360                         } else {
19361                             month += 1;
19362                         }
19363                     }
19364                     //Roo.log([year,month,day]);
19365                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19366                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19367 //                    this.fill();
19368                     //Roo.log(this.formatDate(this.date));
19369                     this.setValue(this.formatDate(this.date));
19370                     this.hidePopup();
19371                 }
19372                 break;
19373         }
19374     },
19375     
19376     setStartDate: function(startDate)
19377     {
19378         this.startDate = startDate || -Infinity;
19379         if (this.startDate !== -Infinity) {
19380             this.startDate = this.parseDate(this.startDate);
19381         }
19382         this.update();
19383         this.updateNavArrows();
19384     },
19385
19386     setEndDate: function(endDate)
19387     {
19388         this.endDate = endDate || Infinity;
19389         if (this.endDate !== Infinity) {
19390             this.endDate = this.parseDate(this.endDate);
19391         }
19392         this.update();
19393         this.updateNavArrows();
19394     },
19395     
19396     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19397     {
19398         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19399         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19400             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19401         }
19402         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19403             return parseInt(d, 10);
19404         });
19405         this.update();
19406         this.updateNavArrows();
19407     },
19408     
19409     updateNavArrows: function() 
19410     {
19411         if(this.singleMode){
19412             return;
19413         }
19414         
19415         var d = new Date(this.viewDate),
19416         year = d.getUTCFullYear(),
19417         month = d.getUTCMonth();
19418         
19419         Roo.each(this.picker().select('.prev', true).elements, function(v){
19420             v.show();
19421             switch (this.viewMode) {
19422                 case 0:
19423
19424                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19425                         v.hide();
19426                     }
19427                     break;
19428                 case 1:
19429                 case 2:
19430                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19431                         v.hide();
19432                     }
19433                     break;
19434             }
19435         });
19436         
19437         Roo.each(this.picker().select('.next', true).elements, function(v){
19438             v.show();
19439             switch (this.viewMode) {
19440                 case 0:
19441
19442                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19443                         v.hide();
19444                     }
19445                     break;
19446                 case 1:
19447                 case 2:
19448                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19449                         v.hide();
19450                     }
19451                     break;
19452             }
19453         })
19454     },
19455     
19456     moveMonth: function(date, dir)
19457     {
19458         if (!dir) {
19459             return date;
19460         }
19461         var new_date = new Date(date.valueOf()),
19462         day = new_date.getUTCDate(),
19463         month = new_date.getUTCMonth(),
19464         mag = Math.abs(dir),
19465         new_month, test;
19466         dir = dir > 0 ? 1 : -1;
19467         if (mag == 1){
19468             test = dir == -1
19469             // If going back one month, make sure month is not current month
19470             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19471             ? function(){
19472                 return new_date.getUTCMonth() == month;
19473             }
19474             // If going forward one month, make sure month is as expected
19475             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19476             : function(){
19477                 return new_date.getUTCMonth() != new_month;
19478             };
19479             new_month = month + dir;
19480             new_date.setUTCMonth(new_month);
19481             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19482             if (new_month < 0 || new_month > 11) {
19483                 new_month = (new_month + 12) % 12;
19484             }
19485         } else {
19486             // For magnitudes >1, move one month at a time...
19487             for (var i=0; i<mag; i++) {
19488                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19489                 new_date = this.moveMonth(new_date, dir);
19490             }
19491             // ...then reset the day, keeping it in the new month
19492             new_month = new_date.getUTCMonth();
19493             new_date.setUTCDate(day);
19494             test = function(){
19495                 return new_month != new_date.getUTCMonth();
19496             };
19497         }
19498         // Common date-resetting loop -- if date is beyond end of month, make it
19499         // end of month
19500         while (test()){
19501             new_date.setUTCDate(--day);
19502             new_date.setUTCMonth(new_month);
19503         }
19504         return new_date;
19505     },
19506
19507     moveYear: function(date, dir)
19508     {
19509         return this.moveMonth(date, dir*12);
19510     },
19511
19512     dateWithinRange: function(date)
19513     {
19514         return date >= this.startDate && date <= this.endDate;
19515     },
19516
19517     
19518     remove: function() 
19519     {
19520         this.picker().remove();
19521     },
19522     
19523     validateValue : function(value)
19524     {
19525         if(this.getVisibilityEl().hasClass('hidden')){
19526             return true;
19527         }
19528         
19529         if(value.length < 1)  {
19530             if(this.allowBlank){
19531                 return true;
19532             }
19533             return false;
19534         }
19535         
19536         if(value.length < this.minLength){
19537             return false;
19538         }
19539         if(value.length > this.maxLength){
19540             return false;
19541         }
19542         if(this.vtype){
19543             var vt = Roo.form.VTypes;
19544             if(!vt[this.vtype](value, this)){
19545                 return false;
19546             }
19547         }
19548         if(typeof this.validator == "function"){
19549             var msg = this.validator(value);
19550             if(msg !== true){
19551                 return false;
19552             }
19553         }
19554         
19555         if(this.regex && !this.regex.test(value)){
19556             return false;
19557         }
19558         
19559         if(typeof(this.parseDate(value)) == 'undefined'){
19560             return false;
19561         }
19562         
19563         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19564             return false;
19565         }      
19566         
19567         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19568             return false;
19569         } 
19570         
19571         
19572         return true;
19573     },
19574     
19575     reset : function()
19576     {
19577         this.date = this.viewDate = '';
19578         
19579         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19580     }
19581    
19582 });
19583
19584 Roo.apply(Roo.bootstrap.DateField,  {
19585     
19586     head : {
19587         tag: 'thead',
19588         cn: [
19589         {
19590             tag: 'tr',
19591             cn: [
19592             {
19593                 tag: 'th',
19594                 cls: 'prev',
19595                 html: '<i class="fa fa-arrow-left"/>'
19596             },
19597             {
19598                 tag: 'th',
19599                 cls: 'switch',
19600                 colspan: '5'
19601             },
19602             {
19603                 tag: 'th',
19604                 cls: 'next',
19605                 html: '<i class="fa fa-arrow-right"/>'
19606             }
19607
19608             ]
19609         }
19610         ]
19611     },
19612     
19613     content : {
19614         tag: 'tbody',
19615         cn: [
19616         {
19617             tag: 'tr',
19618             cn: [
19619             {
19620                 tag: 'td',
19621                 colspan: '7'
19622             }
19623             ]
19624         }
19625         ]
19626     },
19627     
19628     footer : {
19629         tag: 'tfoot',
19630         cn: [
19631         {
19632             tag: 'tr',
19633             cn: [
19634             {
19635                 tag: 'th',
19636                 colspan: '7',
19637                 cls: 'today'
19638             }
19639                     
19640             ]
19641         }
19642         ]
19643     },
19644     
19645     dates:{
19646         en: {
19647             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19648             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19649             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19650             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19651             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19652             today: "Today"
19653         }
19654     },
19655     
19656     modes: [
19657     {
19658         clsName: 'days',
19659         navFnc: 'Month',
19660         navStep: 1
19661     },
19662     {
19663         clsName: 'months',
19664         navFnc: 'FullYear',
19665         navStep: 1
19666     },
19667     {
19668         clsName: 'years',
19669         navFnc: 'FullYear',
19670         navStep: 10
19671     }]
19672 });
19673
19674 Roo.apply(Roo.bootstrap.DateField,  {
19675   
19676     template : {
19677         tag: 'div',
19678         cls: 'datepicker dropdown-menu roo-dynamic',
19679         cn: [
19680         {
19681             tag: 'div',
19682             cls: 'datepicker-days',
19683             cn: [
19684             {
19685                 tag: 'table',
19686                 cls: 'table-condensed',
19687                 cn:[
19688                 Roo.bootstrap.DateField.head,
19689                 {
19690                     tag: 'tbody'
19691                 },
19692                 Roo.bootstrap.DateField.footer
19693                 ]
19694             }
19695             ]
19696         },
19697         {
19698             tag: 'div',
19699             cls: 'datepicker-months',
19700             cn: [
19701             {
19702                 tag: 'table',
19703                 cls: 'table-condensed',
19704                 cn:[
19705                 Roo.bootstrap.DateField.head,
19706                 Roo.bootstrap.DateField.content,
19707                 Roo.bootstrap.DateField.footer
19708                 ]
19709             }
19710             ]
19711         },
19712         {
19713             tag: 'div',
19714             cls: 'datepicker-years',
19715             cn: [
19716             {
19717                 tag: 'table',
19718                 cls: 'table-condensed',
19719                 cn:[
19720                 Roo.bootstrap.DateField.head,
19721                 Roo.bootstrap.DateField.content,
19722                 Roo.bootstrap.DateField.footer
19723                 ]
19724             }
19725             ]
19726         }
19727         ]
19728     }
19729 });
19730
19731  
19732
19733  /*
19734  * - LGPL
19735  *
19736  * TimeField
19737  * 
19738  */
19739
19740 /**
19741  * @class Roo.bootstrap.TimeField
19742  * @extends Roo.bootstrap.Input
19743  * Bootstrap DateField class
19744  * 
19745  * 
19746  * @constructor
19747  * Create a new TimeField
19748  * @param {Object} config The config object
19749  */
19750
19751 Roo.bootstrap.TimeField = function(config){
19752     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19753     this.addEvents({
19754             /**
19755              * @event show
19756              * Fires when this field show.
19757              * @param {Roo.bootstrap.DateField} thisthis
19758              * @param {Mixed} date The date value
19759              */
19760             show : true,
19761             /**
19762              * @event show
19763              * Fires when this field hide.
19764              * @param {Roo.bootstrap.DateField} this
19765              * @param {Mixed} date The date value
19766              */
19767             hide : true,
19768             /**
19769              * @event select
19770              * Fires when select a date.
19771              * @param {Roo.bootstrap.DateField} this
19772              * @param {Mixed} date The date value
19773              */
19774             select : true
19775         });
19776 };
19777
19778 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19779     
19780     /**
19781      * @cfg {String} format
19782      * The default time format string which can be overriden for localization support.  The format must be
19783      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19784      */
19785     format : "H:i",
19786        
19787     onRender: function(ct, position)
19788     {
19789         
19790         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19791                 
19792         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19793         
19794         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19795         
19796         this.pop = this.picker().select('>.datepicker-time',true).first();
19797         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19798         
19799         this.picker().on('mousedown', this.onMousedown, this);
19800         this.picker().on('click', this.onClick, this);
19801         
19802         this.picker().addClass('datepicker-dropdown');
19803     
19804         this.fillTime();
19805         this.update();
19806             
19807         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19808         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19809         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19810         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19811         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19812         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19813
19814     },
19815     
19816     fireKey: function(e){
19817         if (!this.picker().isVisible()){
19818             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19819                 this.show();
19820             }
19821             return;
19822         }
19823
19824         e.preventDefault();
19825         
19826         switch(e.keyCode){
19827             case 27: // escape
19828                 this.hide();
19829                 break;
19830             case 37: // left
19831             case 39: // right
19832                 this.onTogglePeriod();
19833                 break;
19834             case 38: // up
19835                 this.onIncrementMinutes();
19836                 break;
19837             case 40: // down
19838                 this.onDecrementMinutes();
19839                 break;
19840             case 13: // enter
19841             case 9: // tab
19842                 this.setTime();
19843                 break;
19844         }
19845     },
19846     
19847     onClick: function(e) {
19848         e.stopPropagation();
19849         e.preventDefault();
19850     },
19851     
19852     picker : function()
19853     {
19854         return this.el.select('.datepicker', true).first();
19855     },
19856     
19857     fillTime: function()
19858     {    
19859         var time = this.pop.select('tbody', true).first();
19860         
19861         time.dom.innerHTML = '';
19862         
19863         time.createChild({
19864             tag: 'tr',
19865             cn: [
19866                 {
19867                     tag: 'td',
19868                     cn: [
19869                         {
19870                             tag: 'a',
19871                             href: '#',
19872                             cls: 'btn',
19873                             cn: [
19874                                 {
19875                                     tag: 'span',
19876                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19877                                 }
19878                             ]
19879                         } 
19880                     ]
19881                 },
19882                 {
19883                     tag: 'td',
19884                     cls: 'separator'
19885                 },
19886                 {
19887                     tag: 'td',
19888                     cn: [
19889                         {
19890                             tag: 'a',
19891                             href: '#',
19892                             cls: 'btn',
19893                             cn: [
19894                                 {
19895                                     tag: 'span',
19896                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19897                                 }
19898                             ]
19899                         }
19900                     ]
19901                 },
19902                 {
19903                     tag: 'td',
19904                     cls: 'separator'
19905                 }
19906             ]
19907         });
19908         
19909         time.createChild({
19910             tag: 'tr',
19911             cn: [
19912                 {
19913                     tag: 'td',
19914                     cn: [
19915                         {
19916                             tag: 'span',
19917                             cls: 'timepicker-hour',
19918                             html: '00'
19919                         }  
19920                     ]
19921                 },
19922                 {
19923                     tag: 'td',
19924                     cls: 'separator',
19925                     html: ':'
19926                 },
19927                 {
19928                     tag: 'td',
19929                     cn: [
19930                         {
19931                             tag: 'span',
19932                             cls: 'timepicker-minute',
19933                             html: '00'
19934                         }  
19935                     ]
19936                 },
19937                 {
19938                     tag: 'td',
19939                     cls: 'separator'
19940                 },
19941                 {
19942                     tag: 'td',
19943                     cn: [
19944                         {
19945                             tag: 'button',
19946                             type: 'button',
19947                             cls: 'btn btn-primary period',
19948                             html: 'AM'
19949                             
19950                         }
19951                     ]
19952                 }
19953             ]
19954         });
19955         
19956         time.createChild({
19957             tag: 'tr',
19958             cn: [
19959                 {
19960                     tag: 'td',
19961                     cn: [
19962                         {
19963                             tag: 'a',
19964                             href: '#',
19965                             cls: 'btn',
19966                             cn: [
19967                                 {
19968                                     tag: 'span',
19969                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19970                                 }
19971                             ]
19972                         }
19973                     ]
19974                 },
19975                 {
19976                     tag: 'td',
19977                     cls: 'separator'
19978                 },
19979                 {
19980                     tag: 'td',
19981                     cn: [
19982                         {
19983                             tag: 'a',
19984                             href: '#',
19985                             cls: 'btn',
19986                             cn: [
19987                                 {
19988                                     tag: 'span',
19989                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19990                                 }
19991                             ]
19992                         }
19993                     ]
19994                 },
19995                 {
19996                     tag: 'td',
19997                     cls: 'separator'
19998                 }
19999             ]
20000         });
20001         
20002     },
20003     
20004     update: function()
20005     {
20006         
20007         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20008         
20009         this.fill();
20010     },
20011     
20012     fill: function() 
20013     {
20014         var hours = this.time.getHours();
20015         var minutes = this.time.getMinutes();
20016         var period = 'AM';
20017         
20018         if(hours > 11){
20019             period = 'PM';
20020         }
20021         
20022         if(hours == 0){
20023             hours = 12;
20024         }
20025         
20026         
20027         if(hours > 12){
20028             hours = hours - 12;
20029         }
20030         
20031         if(hours < 10){
20032             hours = '0' + hours;
20033         }
20034         
20035         if(minutes < 10){
20036             minutes = '0' + minutes;
20037         }
20038         
20039         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20040         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20041         this.pop.select('button', true).first().dom.innerHTML = period;
20042         
20043     },
20044     
20045     place: function()
20046     {   
20047         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20048         
20049         var cls = ['bottom'];
20050         
20051         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20052             cls.pop();
20053             cls.push('top');
20054         }
20055         
20056         cls.push('right');
20057         
20058         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20059             cls.pop();
20060             cls.push('left');
20061         }
20062         
20063         this.picker().addClass(cls.join('-'));
20064         
20065         var _this = this;
20066         
20067         Roo.each(cls, function(c){
20068             if(c == 'bottom'){
20069                 _this.picker().setTop(_this.inputEl().getHeight());
20070                 return;
20071             }
20072             if(c == 'top'){
20073                 _this.picker().setTop(0 - _this.picker().getHeight());
20074                 return;
20075             }
20076             
20077             if(c == 'left'){
20078                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20079                 return;
20080             }
20081             if(c == 'right'){
20082                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20083                 return;
20084             }
20085         });
20086         
20087     },
20088   
20089     onFocus : function()
20090     {
20091         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20092         this.show();
20093     },
20094     
20095     onBlur : function()
20096     {
20097         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20098         this.hide();
20099     },
20100     
20101     show : function()
20102     {
20103         this.picker().show();
20104         this.pop.show();
20105         this.update();
20106         this.place();
20107         
20108         this.fireEvent('show', this, this.date);
20109     },
20110     
20111     hide : function()
20112     {
20113         this.picker().hide();
20114         this.pop.hide();
20115         
20116         this.fireEvent('hide', this, this.date);
20117     },
20118     
20119     setTime : function()
20120     {
20121         this.hide();
20122         this.setValue(this.time.format(this.format));
20123         
20124         this.fireEvent('select', this, this.date);
20125         
20126         
20127     },
20128     
20129     onMousedown: function(e){
20130         e.stopPropagation();
20131         e.preventDefault();
20132     },
20133     
20134     onIncrementHours: function()
20135     {
20136         Roo.log('onIncrementHours');
20137         this.time = this.time.add(Date.HOUR, 1);
20138         this.update();
20139         
20140     },
20141     
20142     onDecrementHours: function()
20143     {
20144         Roo.log('onDecrementHours');
20145         this.time = this.time.add(Date.HOUR, -1);
20146         this.update();
20147     },
20148     
20149     onIncrementMinutes: function()
20150     {
20151         Roo.log('onIncrementMinutes');
20152         this.time = this.time.add(Date.MINUTE, 1);
20153         this.update();
20154     },
20155     
20156     onDecrementMinutes: function()
20157     {
20158         Roo.log('onDecrementMinutes');
20159         this.time = this.time.add(Date.MINUTE, -1);
20160         this.update();
20161     },
20162     
20163     onTogglePeriod: function()
20164     {
20165         Roo.log('onTogglePeriod');
20166         this.time = this.time.add(Date.HOUR, 12);
20167         this.update();
20168     }
20169     
20170    
20171 });
20172
20173 Roo.apply(Roo.bootstrap.TimeField,  {
20174     
20175     content : {
20176         tag: 'tbody',
20177         cn: [
20178             {
20179                 tag: 'tr',
20180                 cn: [
20181                 {
20182                     tag: 'td',
20183                     colspan: '7'
20184                 }
20185                 ]
20186             }
20187         ]
20188     },
20189     
20190     footer : {
20191         tag: 'tfoot',
20192         cn: [
20193             {
20194                 tag: 'tr',
20195                 cn: [
20196                 {
20197                     tag: 'th',
20198                     colspan: '7',
20199                     cls: '',
20200                     cn: [
20201                         {
20202                             tag: 'button',
20203                             cls: 'btn btn-info ok',
20204                             html: 'OK'
20205                         }
20206                     ]
20207                 }
20208
20209                 ]
20210             }
20211         ]
20212     }
20213 });
20214
20215 Roo.apply(Roo.bootstrap.TimeField,  {
20216   
20217     template : {
20218         tag: 'div',
20219         cls: 'datepicker dropdown-menu',
20220         cn: [
20221             {
20222                 tag: 'div',
20223                 cls: 'datepicker-time',
20224                 cn: [
20225                 {
20226                     tag: 'table',
20227                     cls: 'table-condensed',
20228                     cn:[
20229                     Roo.bootstrap.TimeField.content,
20230                     Roo.bootstrap.TimeField.footer
20231                     ]
20232                 }
20233                 ]
20234             }
20235         ]
20236     }
20237 });
20238
20239  
20240
20241  /*
20242  * - LGPL
20243  *
20244  * MonthField
20245  * 
20246  */
20247
20248 /**
20249  * @class Roo.bootstrap.MonthField
20250  * @extends Roo.bootstrap.Input
20251  * Bootstrap MonthField class
20252  * 
20253  * @cfg {String} language default en
20254  * 
20255  * @constructor
20256  * Create a new MonthField
20257  * @param {Object} config The config object
20258  */
20259
20260 Roo.bootstrap.MonthField = function(config){
20261     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20262     
20263     this.addEvents({
20264         /**
20265          * @event show
20266          * Fires when this field show.
20267          * @param {Roo.bootstrap.MonthField} this
20268          * @param {Mixed} date The date value
20269          */
20270         show : true,
20271         /**
20272          * @event show
20273          * Fires when this field hide.
20274          * @param {Roo.bootstrap.MonthField} this
20275          * @param {Mixed} date The date value
20276          */
20277         hide : true,
20278         /**
20279          * @event select
20280          * Fires when select a date.
20281          * @param {Roo.bootstrap.MonthField} this
20282          * @param {String} oldvalue The old value
20283          * @param {String} newvalue The new value
20284          */
20285         select : true
20286     });
20287 };
20288
20289 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20290     
20291     onRender: function(ct, position)
20292     {
20293         
20294         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20295         
20296         this.language = this.language || 'en';
20297         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20298         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20299         
20300         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20301         this.isInline = false;
20302         this.isInput = true;
20303         this.component = this.el.select('.add-on', true).first() || false;
20304         this.component = (this.component && this.component.length === 0) ? false : this.component;
20305         this.hasInput = this.component && this.inputEL().length;
20306         
20307         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20308         
20309         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20310         
20311         this.picker().on('mousedown', this.onMousedown, this);
20312         this.picker().on('click', this.onClick, this);
20313         
20314         this.picker().addClass('datepicker-dropdown');
20315         
20316         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20317             v.setStyle('width', '189px');
20318         });
20319         
20320         this.fillMonths();
20321         
20322         this.update();
20323         
20324         if(this.isInline) {
20325             this.show();
20326         }
20327         
20328     },
20329     
20330     setValue: function(v, suppressEvent)
20331     {   
20332         var o = this.getValue();
20333         
20334         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20335         
20336         this.update();
20337
20338         if(suppressEvent !== true){
20339             this.fireEvent('select', this, o, v);
20340         }
20341         
20342     },
20343     
20344     getValue: function()
20345     {
20346         return this.value;
20347     },
20348     
20349     onClick: function(e) 
20350     {
20351         e.stopPropagation();
20352         e.preventDefault();
20353         
20354         var target = e.getTarget();
20355         
20356         if(target.nodeName.toLowerCase() === 'i'){
20357             target = Roo.get(target).dom.parentNode;
20358         }
20359         
20360         var nodeName = target.nodeName;
20361         var className = target.className;
20362         var html = target.innerHTML;
20363         
20364         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20365             return;
20366         }
20367         
20368         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20369         
20370         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20371         
20372         this.hide();
20373                         
20374     },
20375     
20376     picker : function()
20377     {
20378         return this.pickerEl;
20379     },
20380     
20381     fillMonths: function()
20382     {    
20383         var i = 0;
20384         var months = this.picker().select('>.datepicker-months td', true).first();
20385         
20386         months.dom.innerHTML = '';
20387         
20388         while (i < 12) {
20389             var month = {
20390                 tag: 'span',
20391                 cls: 'month',
20392                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20393             };
20394             
20395             months.createChild(month);
20396         }
20397         
20398     },
20399     
20400     update: function()
20401     {
20402         var _this = this;
20403         
20404         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20405             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20406         }
20407         
20408         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20409             e.removeClass('active');
20410             
20411             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20412                 e.addClass('active');
20413             }
20414         })
20415     },
20416     
20417     place: function()
20418     {
20419         if(this.isInline) {
20420             return;
20421         }
20422         
20423         this.picker().removeClass(['bottom', 'top']);
20424         
20425         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20426             /*
20427              * place to the top of element!
20428              *
20429              */
20430             
20431             this.picker().addClass('top');
20432             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20433             
20434             return;
20435         }
20436         
20437         this.picker().addClass('bottom');
20438         
20439         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20440     },
20441     
20442     onFocus : function()
20443     {
20444         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20445         this.show();
20446     },
20447     
20448     onBlur : function()
20449     {
20450         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20451         
20452         var d = this.inputEl().getValue();
20453         
20454         this.setValue(d);
20455                 
20456         this.hide();
20457     },
20458     
20459     show : function()
20460     {
20461         this.picker().show();
20462         this.picker().select('>.datepicker-months', true).first().show();
20463         this.update();
20464         this.place();
20465         
20466         this.fireEvent('show', this, this.date);
20467     },
20468     
20469     hide : function()
20470     {
20471         if(this.isInline) {
20472             return;
20473         }
20474         this.picker().hide();
20475         this.fireEvent('hide', this, this.date);
20476         
20477     },
20478     
20479     onMousedown: function(e)
20480     {
20481         e.stopPropagation();
20482         e.preventDefault();
20483     },
20484     
20485     keyup: function(e)
20486     {
20487         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20488         this.update();
20489     },
20490
20491     fireKey: function(e)
20492     {
20493         if (!this.picker().isVisible()){
20494             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20495                 this.show();
20496             }
20497             return;
20498         }
20499         
20500         var dir;
20501         
20502         switch(e.keyCode){
20503             case 27: // escape
20504                 this.hide();
20505                 e.preventDefault();
20506                 break;
20507             case 37: // left
20508             case 39: // right
20509                 dir = e.keyCode == 37 ? -1 : 1;
20510                 
20511                 this.vIndex = this.vIndex + dir;
20512                 
20513                 if(this.vIndex < 0){
20514                     this.vIndex = 0;
20515                 }
20516                 
20517                 if(this.vIndex > 11){
20518                     this.vIndex = 11;
20519                 }
20520                 
20521                 if(isNaN(this.vIndex)){
20522                     this.vIndex = 0;
20523                 }
20524                 
20525                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20526                 
20527                 break;
20528             case 38: // up
20529             case 40: // down
20530                 
20531                 dir = e.keyCode == 38 ? -1 : 1;
20532                 
20533                 this.vIndex = this.vIndex + dir * 4;
20534                 
20535                 if(this.vIndex < 0){
20536                     this.vIndex = 0;
20537                 }
20538                 
20539                 if(this.vIndex > 11){
20540                     this.vIndex = 11;
20541                 }
20542                 
20543                 if(isNaN(this.vIndex)){
20544                     this.vIndex = 0;
20545                 }
20546                 
20547                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20548                 break;
20549                 
20550             case 13: // enter
20551                 
20552                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20553                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20554                 }
20555                 
20556                 this.hide();
20557                 e.preventDefault();
20558                 break;
20559             case 9: // tab
20560                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20561                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20562                 }
20563                 this.hide();
20564                 break;
20565             case 16: // shift
20566             case 17: // ctrl
20567             case 18: // alt
20568                 break;
20569             default :
20570                 this.hide();
20571                 
20572         }
20573     },
20574     
20575     remove: function() 
20576     {
20577         this.picker().remove();
20578     }
20579    
20580 });
20581
20582 Roo.apply(Roo.bootstrap.MonthField,  {
20583     
20584     content : {
20585         tag: 'tbody',
20586         cn: [
20587         {
20588             tag: 'tr',
20589             cn: [
20590             {
20591                 tag: 'td',
20592                 colspan: '7'
20593             }
20594             ]
20595         }
20596         ]
20597     },
20598     
20599     dates:{
20600         en: {
20601             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20602             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20603         }
20604     }
20605 });
20606
20607 Roo.apply(Roo.bootstrap.MonthField,  {
20608   
20609     template : {
20610         tag: 'div',
20611         cls: 'datepicker dropdown-menu roo-dynamic',
20612         cn: [
20613             {
20614                 tag: 'div',
20615                 cls: 'datepicker-months',
20616                 cn: [
20617                 {
20618                     tag: 'table',
20619                     cls: 'table-condensed',
20620                     cn:[
20621                         Roo.bootstrap.DateField.content
20622                     ]
20623                 }
20624                 ]
20625             }
20626         ]
20627     }
20628 });
20629
20630  
20631
20632  
20633  /*
20634  * - LGPL
20635  *
20636  * CheckBox
20637  * 
20638  */
20639
20640 /**
20641  * @class Roo.bootstrap.CheckBox
20642  * @extends Roo.bootstrap.Input
20643  * Bootstrap CheckBox class
20644  * 
20645  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20646  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20647  * @cfg {String} boxLabel The text that appears beside the checkbox
20648  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20649  * @cfg {Boolean} checked initnal the element
20650  * @cfg {Boolean} inline inline the element (default false)
20651  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20652  * @cfg {String} tooltip label tooltip
20653  * 
20654  * @constructor
20655  * Create a new CheckBox
20656  * @param {Object} config The config object
20657  */
20658
20659 Roo.bootstrap.CheckBox = function(config){
20660     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20661    
20662     this.addEvents({
20663         /**
20664         * @event check
20665         * Fires when the element is checked or unchecked.
20666         * @param {Roo.bootstrap.CheckBox} this This input
20667         * @param {Boolean} checked The new checked value
20668         */
20669        check : true,
20670        /**
20671         * @event click
20672         * Fires when the element is click.
20673         * @param {Roo.bootstrap.CheckBox} this This input
20674         */
20675        click : true
20676     });
20677     
20678 };
20679
20680 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20681   
20682     inputType: 'checkbox',
20683     inputValue: 1,
20684     valueOff: 0,
20685     boxLabel: false,
20686     checked: false,
20687     weight : false,
20688     inline: false,
20689     tooltip : '',
20690     
20691     getAutoCreate : function()
20692     {
20693         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20694         
20695         var id = Roo.id();
20696         
20697         var cfg = {};
20698         
20699         cfg.cls = 'form-group ' + this.inputType; //input-group
20700         
20701         if(this.inline){
20702             cfg.cls += ' ' + this.inputType + '-inline';
20703         }
20704         
20705         var input =  {
20706             tag: 'input',
20707             id : id,
20708             type : this.inputType,
20709             value : this.inputValue,
20710             cls : 'roo-' + this.inputType, //'form-box',
20711             placeholder : this.placeholder || ''
20712             
20713         };
20714         
20715         if(this.inputType != 'radio'){
20716             var hidden =  {
20717                 tag: 'input',
20718                 type : 'hidden',
20719                 cls : 'roo-hidden-value',
20720                 value : this.checked ? this.inputValue : this.valueOff
20721             };
20722         }
20723         
20724             
20725         if (this.weight) { // Validity check?
20726             cfg.cls += " " + this.inputType + "-" + this.weight;
20727         }
20728         
20729         if (this.disabled) {
20730             input.disabled=true;
20731         }
20732         
20733         if(this.checked){
20734             input.checked = this.checked;
20735         }
20736         
20737         if (this.name) {
20738             
20739             input.name = this.name;
20740             
20741             if(this.inputType != 'radio'){
20742                 hidden.name = this.name;
20743                 input.name = '_hidden_' + this.name;
20744             }
20745         }
20746         
20747         if (this.size) {
20748             input.cls += ' input-' + this.size;
20749         }
20750         
20751         var settings=this;
20752         
20753         ['xs','sm','md','lg'].map(function(size){
20754             if (settings[size]) {
20755                 cfg.cls += ' col-' + size + '-' + settings[size];
20756             }
20757         });
20758         
20759         var inputblock = input;
20760          
20761         if (this.before || this.after) {
20762             
20763             inputblock = {
20764                 cls : 'input-group',
20765                 cn :  [] 
20766             };
20767             
20768             if (this.before) {
20769                 inputblock.cn.push({
20770                     tag :'span',
20771                     cls : 'input-group-addon',
20772                     html : this.before
20773                 });
20774             }
20775             
20776             inputblock.cn.push(input);
20777             
20778             if(this.inputType != 'radio'){
20779                 inputblock.cn.push(hidden);
20780             }
20781             
20782             if (this.after) {
20783                 inputblock.cn.push({
20784                     tag :'span',
20785                     cls : 'input-group-addon',
20786                     html : this.after
20787                 });
20788             }
20789             
20790         }
20791         
20792         if (align ==='left' && this.fieldLabel.length) {
20793 //                Roo.log("left and has label");
20794             cfg.cn = [
20795                 {
20796                     tag: 'label',
20797                     'for' :  id,
20798                     cls : 'control-label',
20799                     html : this.fieldLabel
20800                 },
20801                 {
20802                     cls : "", 
20803                     cn: [
20804                         inputblock
20805                     ]
20806                 }
20807             ];
20808             
20809             if(this.labelWidth > 12){
20810                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20811             }
20812             
20813             if(this.labelWidth < 13 && this.labelmd == 0){
20814                 this.labelmd = this.labelWidth;
20815             }
20816             
20817             if(this.labellg > 0){
20818                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20819                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20820             }
20821             
20822             if(this.labelmd > 0){
20823                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20824                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20825             }
20826             
20827             if(this.labelsm > 0){
20828                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20829                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20830             }
20831             
20832             if(this.labelxs > 0){
20833                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20834                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20835             }
20836             
20837         } else if ( this.fieldLabel.length) {
20838 //                Roo.log(" label");
20839                 cfg.cn = [
20840                    
20841                     {
20842                         tag: this.boxLabel ? 'span' : 'label',
20843                         'for': id,
20844                         cls: 'control-label box-input-label',
20845                         //cls : 'input-group-addon',
20846                         html : this.fieldLabel
20847                     },
20848                     
20849                     inputblock
20850                     
20851                 ];
20852
20853         } else {
20854             
20855 //                Roo.log(" no label && no align");
20856                 cfg.cn = [  inputblock ] ;
20857                 
20858                 
20859         }
20860         
20861         if(this.boxLabel){
20862              var boxLabelCfg = {
20863                 tag: 'label',
20864                 //'for': id, // box label is handled by onclick - so no for...
20865                 cls: 'box-label',
20866                 html: this.boxLabel
20867             };
20868             
20869             if(this.tooltip){
20870                 boxLabelCfg.tooltip = this.tooltip;
20871             }
20872              
20873             cfg.cn.push(boxLabelCfg);
20874         }
20875         
20876         if(this.inputType != 'radio'){
20877             cfg.cn.push(hidden);
20878         }
20879         
20880         return cfg;
20881         
20882     },
20883     
20884     /**
20885      * return the real input element.
20886      */
20887     inputEl: function ()
20888     {
20889         return this.el.select('input.roo-' + this.inputType,true).first();
20890     },
20891     hiddenEl: function ()
20892     {
20893         return this.el.select('input.roo-hidden-value',true).first();
20894     },
20895     
20896     labelEl: function()
20897     {
20898         return this.el.select('label.control-label',true).first();
20899     },
20900     /* depricated... */
20901     
20902     label: function()
20903     {
20904         return this.labelEl();
20905     },
20906     
20907     boxLabelEl: function()
20908     {
20909         return this.el.select('label.box-label',true).first();
20910     },
20911     
20912     initEvents : function()
20913     {
20914 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20915         
20916         this.inputEl().on('click', this.onClick,  this);
20917         
20918         if (this.boxLabel) { 
20919             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20920         }
20921         
20922         this.startValue = this.getValue();
20923         
20924         if(this.groupId){
20925             Roo.bootstrap.CheckBox.register(this);
20926         }
20927     },
20928     
20929     onClick : function(e)
20930     {   
20931         if(this.fireEvent('click', this, e) !== false){
20932             this.setChecked(!this.checked);
20933         }
20934         
20935     },
20936     
20937     setChecked : function(state,suppressEvent)
20938     {
20939         this.startValue = this.getValue();
20940
20941         if(this.inputType == 'radio'){
20942             
20943             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20944                 e.dom.checked = false;
20945             });
20946             
20947             this.inputEl().dom.checked = true;
20948             
20949             this.inputEl().dom.value = this.inputValue;
20950             
20951             if(suppressEvent !== true){
20952                 this.fireEvent('check', this, true);
20953             }
20954             
20955             this.validate();
20956             
20957             return;
20958         }
20959         
20960         this.checked = state;
20961         
20962         this.inputEl().dom.checked = state;
20963         
20964         
20965         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20966         
20967         if(suppressEvent !== true){
20968             this.fireEvent('check', this, state);
20969         }
20970         
20971         this.validate();
20972     },
20973     
20974     getValue : function()
20975     {
20976         if(this.inputType == 'radio'){
20977             return this.getGroupValue();
20978         }
20979         
20980         return this.hiddenEl().dom.value;
20981         
20982     },
20983     
20984     getGroupValue : function()
20985     {
20986         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20987             return '';
20988         }
20989         
20990         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20991     },
20992     
20993     setValue : function(v,suppressEvent)
20994     {
20995         if(this.inputType == 'radio'){
20996             this.setGroupValue(v, suppressEvent);
20997             return;
20998         }
20999         
21000         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21001         
21002         this.validate();
21003     },
21004     
21005     setGroupValue : function(v, suppressEvent)
21006     {
21007         this.startValue = this.getValue();
21008         
21009         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21010             e.dom.checked = false;
21011             
21012             if(e.dom.value == v){
21013                 e.dom.checked = true;
21014             }
21015         });
21016         
21017         if(suppressEvent !== true){
21018             this.fireEvent('check', this, true);
21019         }
21020
21021         this.validate();
21022         
21023         return;
21024     },
21025     
21026     validate : function()
21027     {
21028         if(this.getVisibilityEl().hasClass('hidden')){
21029             return true;
21030         }
21031         
21032         if(
21033                 this.disabled || 
21034                 (this.inputType == 'radio' && this.validateRadio()) ||
21035                 (this.inputType == 'checkbox' && this.validateCheckbox())
21036         ){
21037             this.markValid();
21038             return true;
21039         }
21040         
21041         this.markInvalid();
21042         return false;
21043     },
21044     
21045     validateRadio : function()
21046     {
21047         if(this.getVisibilityEl().hasClass('hidden')){
21048             return true;
21049         }
21050         
21051         if(this.allowBlank){
21052             return true;
21053         }
21054         
21055         var valid = false;
21056         
21057         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21058             if(!e.dom.checked){
21059                 return;
21060             }
21061             
21062             valid = true;
21063             
21064             return false;
21065         });
21066         
21067         return valid;
21068     },
21069     
21070     validateCheckbox : function()
21071     {
21072         if(!this.groupId){
21073             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21074             //return (this.getValue() == this.inputValue) ? true : false;
21075         }
21076         
21077         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21078         
21079         if(!group){
21080             return false;
21081         }
21082         
21083         var r = false;
21084         
21085         for(var i in group){
21086             if(group[i].el.isVisible(true)){
21087                 r = false;
21088                 break;
21089             }
21090             
21091             r = true;
21092         }
21093         
21094         for(var i in group){
21095             if(r){
21096                 break;
21097             }
21098             
21099             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21100         }
21101         
21102         return r;
21103     },
21104     
21105     /**
21106      * Mark this field as valid
21107      */
21108     markValid : function()
21109     {
21110         var _this = this;
21111         
21112         this.fireEvent('valid', this);
21113         
21114         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21115         
21116         if(this.groupId){
21117             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21118         }
21119         
21120         if(label){
21121             label.markValid();
21122         }
21123
21124         if(this.inputType == 'radio'){
21125             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21126                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21127                 e.findParent('.form-group', false, true).addClass(_this.validClass);
21128             });
21129             
21130             return;
21131         }
21132
21133         if(!this.groupId){
21134             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21135             this.el.findParent('.form-group', false, true).addClass(this.validClass);
21136             return;
21137         }
21138         
21139         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21140         
21141         if(!group){
21142             return;
21143         }
21144         
21145         for(var i in group){
21146             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21147             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
21148         }
21149     },
21150     
21151      /**
21152      * Mark this field as invalid
21153      * @param {String} msg The validation message
21154      */
21155     markInvalid : function(msg)
21156     {
21157         if(this.allowBlank){
21158             return;
21159         }
21160         
21161         var _this = this;
21162         
21163         this.fireEvent('invalid', this, msg);
21164         
21165         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21166         
21167         if(this.groupId){
21168             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21169         }
21170         
21171         if(label){
21172             label.markInvalid();
21173         }
21174             
21175         if(this.inputType == 'radio'){
21176             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21177                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
21178                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
21179             });
21180             
21181             return;
21182         }
21183         
21184         if(!this.groupId){
21185             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21186             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
21187             return;
21188         }
21189         
21190         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21191         
21192         if(!group){
21193             return;
21194         }
21195         
21196         for(var i in group){
21197             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21198             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
21199         }
21200         
21201     },
21202     
21203     clearInvalid : function()
21204     {
21205         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21206         
21207         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21208         
21209         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21210         
21211         if (label && label.iconEl) {
21212             label.iconEl.removeClass(label.validClass);
21213             label.iconEl.removeClass(label.invalidClass);
21214         }
21215     },
21216     
21217     disable : function()
21218     {
21219         if(this.inputType != 'radio'){
21220             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21221             return;
21222         }
21223         
21224         var _this = this;
21225         
21226         if(this.rendered){
21227             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21228                 _this.getActionEl().addClass(this.disabledClass);
21229                 e.dom.disabled = true;
21230             });
21231         }
21232         
21233         this.disabled = true;
21234         this.fireEvent("disable", this);
21235         return this;
21236     },
21237
21238     enable : function()
21239     {
21240         if(this.inputType != 'radio'){
21241             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21242             return;
21243         }
21244         
21245         var _this = this;
21246         
21247         if(this.rendered){
21248             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21249                 _this.getActionEl().removeClass(this.disabledClass);
21250                 e.dom.disabled = false;
21251             });
21252         }
21253         
21254         this.disabled = false;
21255         this.fireEvent("enable", this);
21256         return this;
21257     },
21258     
21259     setBoxLabel : function(v)
21260     {
21261         this.boxLabel = v;
21262         
21263         if(this.rendered){
21264             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21265         }
21266     }
21267
21268 });
21269
21270 Roo.apply(Roo.bootstrap.CheckBox, {
21271     
21272     groups: {},
21273     
21274      /**
21275     * register a CheckBox Group
21276     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21277     */
21278     register : function(checkbox)
21279     {
21280         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21281             this.groups[checkbox.groupId] = {};
21282         }
21283         
21284         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21285             return;
21286         }
21287         
21288         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21289         
21290     },
21291     /**
21292     * fetch a CheckBox Group based on the group ID
21293     * @param {string} the group ID
21294     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21295     */
21296     get: function(groupId) {
21297         if (typeof(this.groups[groupId]) == 'undefined') {
21298             return false;
21299         }
21300         
21301         return this.groups[groupId] ;
21302     }
21303     
21304     
21305 });
21306 /*
21307  * - LGPL
21308  *
21309  * RadioItem
21310  * 
21311  */
21312
21313 /**
21314  * @class Roo.bootstrap.Radio
21315  * @extends Roo.bootstrap.Component
21316  * Bootstrap Radio class
21317  * @cfg {String} boxLabel - the label associated
21318  * @cfg {String} value - the value of radio
21319  * 
21320  * @constructor
21321  * Create a new Radio
21322  * @param {Object} config The config object
21323  */
21324 Roo.bootstrap.Radio = function(config){
21325     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21326     
21327 };
21328
21329 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21330     
21331     boxLabel : '',
21332     
21333     value : '',
21334     
21335     getAutoCreate : function()
21336     {
21337         var cfg = {
21338             tag : 'div',
21339             cls : 'form-group radio',
21340             cn : [
21341                 {
21342                     tag : 'label',
21343                     cls : 'box-label',
21344                     html : this.boxLabel
21345                 }
21346             ]
21347         };
21348         
21349         return cfg;
21350     },
21351     
21352     initEvents : function() 
21353     {
21354         this.parent().register(this);
21355         
21356         this.el.on('click', this.onClick, this);
21357         
21358     },
21359     
21360     onClick : function(e)
21361     {
21362         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21363             this.setChecked(true);
21364         }
21365     },
21366     
21367     setChecked : function(state, suppressEvent)
21368     {
21369         this.parent().setValue(this.value, suppressEvent);
21370         
21371     },
21372     
21373     setBoxLabel : function(v)
21374     {
21375         this.boxLabel = v;
21376         
21377         if(this.rendered){
21378             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21379         }
21380     }
21381     
21382 });
21383  
21384
21385  /*
21386  * - LGPL
21387  *
21388  * Input
21389  * 
21390  */
21391
21392 /**
21393  * @class Roo.bootstrap.SecurePass
21394  * @extends Roo.bootstrap.Input
21395  * Bootstrap SecurePass class
21396  *
21397  * 
21398  * @constructor
21399  * Create a new SecurePass
21400  * @param {Object} config The config object
21401  */
21402  
21403 Roo.bootstrap.SecurePass = function (config) {
21404     // these go here, so the translation tool can replace them..
21405     this.errors = {
21406         PwdEmpty: "Please type a password, and then retype it to confirm.",
21407         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21408         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21409         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21410         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21411         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21412         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21413         TooWeak: "Your password is Too Weak."
21414     },
21415     this.meterLabel = "Password strength:";
21416     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21417     this.meterClass = [
21418         "roo-password-meter-tooweak", 
21419         "roo-password-meter-weak", 
21420         "roo-password-meter-medium", 
21421         "roo-password-meter-strong", 
21422         "roo-password-meter-grey"
21423     ];
21424     
21425     this.errors = {};
21426     
21427     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21428 }
21429
21430 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21431     /**
21432      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21433      * {
21434      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21435      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21436      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21437      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21438      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21439      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21440      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21441      * })
21442      */
21443     // private
21444     
21445     meterWidth: 300,
21446     errorMsg :'',    
21447     errors: false,
21448     imageRoot: '/',
21449     /**
21450      * @cfg {String/Object} Label for the strength meter (defaults to
21451      * 'Password strength:')
21452      */
21453     // private
21454     meterLabel: '',
21455     /**
21456      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21457      * ['Weak', 'Medium', 'Strong'])
21458      */
21459     // private    
21460     pwdStrengths: false,    
21461     // private
21462     strength: 0,
21463     // private
21464     _lastPwd: null,
21465     // private
21466     kCapitalLetter: 0,
21467     kSmallLetter: 1,
21468     kDigit: 2,
21469     kPunctuation: 3,
21470     
21471     insecure: false,
21472     // private
21473     initEvents: function ()
21474     {
21475         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21476
21477         if (this.el.is('input[type=password]') && Roo.isSafari) {
21478             this.el.on('keydown', this.SafariOnKeyDown, this);
21479         }
21480
21481         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21482     },
21483     // private
21484     onRender: function (ct, position)
21485     {
21486         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21487         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21488         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21489
21490         this.trigger.createChild({
21491                    cn: [
21492                     {
21493                     //id: 'PwdMeter',
21494                     tag: 'div',
21495                     cls: 'roo-password-meter-grey col-xs-12',
21496                     style: {
21497                         //width: 0,
21498                         //width: this.meterWidth + 'px'                                                
21499                         }
21500                     },
21501                     {                            
21502                          cls: 'roo-password-meter-text'                          
21503                     }
21504                 ]            
21505         });
21506
21507          
21508         if (this.hideTrigger) {
21509             this.trigger.setDisplayed(false);
21510         }
21511         this.setSize(this.width || '', this.height || '');
21512     },
21513     // private
21514     onDestroy: function ()
21515     {
21516         if (this.trigger) {
21517             this.trigger.removeAllListeners();
21518             this.trigger.remove();
21519         }
21520         if (this.wrap) {
21521             this.wrap.remove();
21522         }
21523         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21524     },
21525     // private
21526     checkStrength: function ()
21527     {
21528         var pwd = this.inputEl().getValue();
21529         if (pwd == this._lastPwd) {
21530             return;
21531         }
21532
21533         var strength;
21534         if (this.ClientSideStrongPassword(pwd)) {
21535             strength = 3;
21536         } else if (this.ClientSideMediumPassword(pwd)) {
21537             strength = 2;
21538         } else if (this.ClientSideWeakPassword(pwd)) {
21539             strength = 1;
21540         } else {
21541             strength = 0;
21542         }
21543         
21544         Roo.log('strength1: ' + strength);
21545         
21546         //var pm = this.trigger.child('div/div/div').dom;
21547         var pm = this.trigger.child('div/div');
21548         pm.removeClass(this.meterClass);
21549         pm.addClass(this.meterClass[strength]);
21550                 
21551         
21552         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21553                 
21554         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21555         
21556         this._lastPwd = pwd;
21557     },
21558     reset: function ()
21559     {
21560         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21561         
21562         this._lastPwd = '';
21563         
21564         var pm = this.trigger.child('div/div');
21565         pm.removeClass(this.meterClass);
21566         pm.addClass('roo-password-meter-grey');        
21567         
21568         
21569         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21570         
21571         pt.innerHTML = '';
21572         this.inputEl().dom.type='password';
21573     },
21574     // private
21575     validateValue: function (value)
21576     {
21577         
21578         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21579             return false;
21580         }
21581         if (value.length == 0) {
21582             if (this.allowBlank) {
21583                 this.clearInvalid();
21584                 return true;
21585             }
21586
21587             this.markInvalid(this.errors.PwdEmpty);
21588             this.errorMsg = this.errors.PwdEmpty;
21589             return false;
21590         }
21591         
21592         if(this.insecure){
21593             return true;
21594         }
21595         
21596         if ('[\x21-\x7e]*'.match(value)) {
21597             this.markInvalid(this.errors.PwdBadChar);
21598             this.errorMsg = this.errors.PwdBadChar;
21599             return false;
21600         }
21601         if (value.length < 6) {
21602             this.markInvalid(this.errors.PwdShort);
21603             this.errorMsg = this.errors.PwdShort;
21604             return false;
21605         }
21606         if (value.length > 16) {
21607             this.markInvalid(this.errors.PwdLong);
21608             this.errorMsg = this.errors.PwdLong;
21609             return false;
21610         }
21611         var strength;
21612         if (this.ClientSideStrongPassword(value)) {
21613             strength = 3;
21614         } else if (this.ClientSideMediumPassword(value)) {
21615             strength = 2;
21616         } else if (this.ClientSideWeakPassword(value)) {
21617             strength = 1;
21618         } else {
21619             strength = 0;
21620         }
21621
21622         
21623         if (strength < 2) {
21624             //this.markInvalid(this.errors.TooWeak);
21625             this.errorMsg = this.errors.TooWeak;
21626             //return false;
21627         }
21628         
21629         
21630         console.log('strength2: ' + strength);
21631         
21632         //var pm = this.trigger.child('div/div/div').dom;
21633         
21634         var pm = this.trigger.child('div/div');
21635         pm.removeClass(this.meterClass);
21636         pm.addClass(this.meterClass[strength]);
21637                 
21638         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21639                 
21640         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21641         
21642         this.errorMsg = ''; 
21643         return true;
21644     },
21645     // private
21646     CharacterSetChecks: function (type)
21647     {
21648         this.type = type;
21649         this.fResult = false;
21650     },
21651     // private
21652     isctype: function (character, type)
21653     {
21654         switch (type) {  
21655             case this.kCapitalLetter:
21656                 if (character >= 'A' && character <= 'Z') {
21657                     return true;
21658                 }
21659                 break;
21660             
21661             case this.kSmallLetter:
21662                 if (character >= 'a' && character <= 'z') {
21663                     return true;
21664                 }
21665                 break;
21666             
21667             case this.kDigit:
21668                 if (character >= '0' && character <= '9') {
21669                     return true;
21670                 }
21671                 break;
21672             
21673             case this.kPunctuation:
21674                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21675                     return true;
21676                 }
21677                 break;
21678             
21679             default:
21680                 return false;
21681         }
21682
21683     },
21684     // private
21685     IsLongEnough: function (pwd, size)
21686     {
21687         return !(pwd == null || isNaN(size) || pwd.length < size);
21688     },
21689     // private
21690     SpansEnoughCharacterSets: function (word, nb)
21691     {
21692         if (!this.IsLongEnough(word, nb))
21693         {
21694             return false;
21695         }
21696
21697         var characterSetChecks = new Array(
21698             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21699             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21700         );
21701         
21702         for (var index = 0; index < word.length; ++index) {
21703             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21704                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21705                     characterSetChecks[nCharSet].fResult = true;
21706                     break;
21707                 }
21708             }
21709         }
21710
21711         var nCharSets = 0;
21712         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21713             if (characterSetChecks[nCharSet].fResult) {
21714                 ++nCharSets;
21715             }
21716         }
21717
21718         if (nCharSets < nb) {
21719             return false;
21720         }
21721         return true;
21722     },
21723     // private
21724     ClientSideStrongPassword: function (pwd)
21725     {
21726         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21727     },
21728     // private
21729     ClientSideMediumPassword: function (pwd)
21730     {
21731         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21732     },
21733     // private
21734     ClientSideWeakPassword: function (pwd)
21735     {
21736         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21737     }
21738           
21739 })//<script type="text/javascript">
21740
21741 /*
21742  * Based  Ext JS Library 1.1.1
21743  * Copyright(c) 2006-2007, Ext JS, LLC.
21744  * LGPL
21745  *
21746  */
21747  
21748 /**
21749  * @class Roo.HtmlEditorCore
21750  * @extends Roo.Component
21751  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21752  *
21753  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21754  */
21755
21756 Roo.HtmlEditorCore = function(config){
21757     
21758     
21759     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21760     
21761     
21762     this.addEvents({
21763         /**
21764          * @event initialize
21765          * Fires when the editor is fully initialized (including the iframe)
21766          * @param {Roo.HtmlEditorCore} this
21767          */
21768         initialize: true,
21769         /**
21770          * @event activate
21771          * Fires when the editor is first receives the focus. Any insertion must wait
21772          * until after this event.
21773          * @param {Roo.HtmlEditorCore} this
21774          */
21775         activate: true,
21776          /**
21777          * @event beforesync
21778          * Fires before the textarea is updated with content from the editor iframe. Return false
21779          * to cancel the sync.
21780          * @param {Roo.HtmlEditorCore} this
21781          * @param {String} html
21782          */
21783         beforesync: true,
21784          /**
21785          * @event beforepush
21786          * Fires before the iframe editor is updated with content from the textarea. Return false
21787          * to cancel the push.
21788          * @param {Roo.HtmlEditorCore} this
21789          * @param {String} html
21790          */
21791         beforepush: true,
21792          /**
21793          * @event sync
21794          * Fires when the textarea is updated with content from the editor iframe.
21795          * @param {Roo.HtmlEditorCore} this
21796          * @param {String} html
21797          */
21798         sync: true,
21799          /**
21800          * @event push
21801          * Fires when the iframe editor is updated with content from the textarea.
21802          * @param {Roo.HtmlEditorCore} this
21803          * @param {String} html
21804          */
21805         push: true,
21806         
21807         /**
21808          * @event editorevent
21809          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21810          * @param {Roo.HtmlEditorCore} this
21811          */
21812         editorevent: true
21813         
21814     });
21815     
21816     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21817     
21818     // defaults : white / black...
21819     this.applyBlacklists();
21820     
21821     
21822     
21823 };
21824
21825
21826 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21827
21828
21829      /**
21830      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21831      */
21832     
21833     owner : false,
21834     
21835      /**
21836      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21837      *                        Roo.resizable.
21838      */
21839     resizable : false,
21840      /**
21841      * @cfg {Number} height (in pixels)
21842      */   
21843     height: 300,
21844    /**
21845      * @cfg {Number} width (in pixels)
21846      */   
21847     width: 500,
21848     
21849     /**
21850      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21851      * 
21852      */
21853     stylesheets: false,
21854     
21855     // id of frame..
21856     frameId: false,
21857     
21858     // private properties
21859     validationEvent : false,
21860     deferHeight: true,
21861     initialized : false,
21862     activated : false,
21863     sourceEditMode : false,
21864     onFocus : Roo.emptyFn,
21865     iframePad:3,
21866     hideMode:'offsets',
21867     
21868     clearUp: true,
21869     
21870     // blacklist + whitelisted elements..
21871     black: false,
21872     white: false,
21873      
21874     bodyCls : '',
21875
21876     /**
21877      * Protected method that will not generally be called directly. It
21878      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21879      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21880      */
21881     getDocMarkup : function(){
21882         // body styles..
21883         var st = '';
21884         
21885         // inherit styels from page...?? 
21886         if (this.stylesheets === false) {
21887             
21888             Roo.get(document.head).select('style').each(function(node) {
21889                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21890             });
21891             
21892             Roo.get(document.head).select('link').each(function(node) { 
21893                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21894             });
21895             
21896         } else if (!this.stylesheets.length) {
21897                 // simple..
21898                 st = '<style type="text/css">' +
21899                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21900                    '</style>';
21901         } else { 
21902             st = '<style type="text/css">' +
21903                     this.stylesheets +
21904                 '</style>';
21905         }
21906         
21907         st +=  '<style type="text/css">' +
21908             'IMG { cursor: pointer } ' +
21909         '</style>';
21910
21911         var cls = 'roo-htmleditor-body';
21912         
21913         if(this.bodyCls.length){
21914             cls += ' ' + this.bodyCls;
21915         }
21916         
21917         return '<html><head>' + st  +
21918             //<style type="text/css">' +
21919             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21920             //'</style>' +
21921             ' </head><body class="' +  cls + '"></body></html>';
21922     },
21923
21924     // private
21925     onRender : function(ct, position)
21926     {
21927         var _t = this;
21928         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21929         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21930         
21931         
21932         this.el.dom.style.border = '0 none';
21933         this.el.dom.setAttribute('tabIndex', -1);
21934         this.el.addClass('x-hidden hide');
21935         
21936         
21937         
21938         if(Roo.isIE){ // fix IE 1px bogus margin
21939             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21940         }
21941        
21942         
21943         this.frameId = Roo.id();
21944         
21945          
21946         
21947         var iframe = this.owner.wrap.createChild({
21948             tag: 'iframe',
21949             cls: 'form-control', // bootstrap..
21950             id: this.frameId,
21951             name: this.frameId,
21952             frameBorder : 'no',
21953             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21954         }, this.el
21955         );
21956         
21957         
21958         this.iframe = iframe.dom;
21959
21960          this.assignDocWin();
21961         
21962         this.doc.designMode = 'on';
21963        
21964         this.doc.open();
21965         this.doc.write(this.getDocMarkup());
21966         this.doc.close();
21967
21968         
21969         var task = { // must defer to wait for browser to be ready
21970             run : function(){
21971                 //console.log("run task?" + this.doc.readyState);
21972                 this.assignDocWin();
21973                 if(this.doc.body || this.doc.readyState == 'complete'){
21974                     try {
21975                         this.doc.designMode="on";
21976                     } catch (e) {
21977                         return;
21978                     }
21979                     Roo.TaskMgr.stop(task);
21980                     this.initEditor.defer(10, this);
21981                 }
21982             },
21983             interval : 10,
21984             duration: 10000,
21985             scope: this
21986         };
21987         Roo.TaskMgr.start(task);
21988
21989     },
21990
21991     // private
21992     onResize : function(w, h)
21993     {
21994          Roo.log('resize: ' +w + ',' + h );
21995         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21996         if(!this.iframe){
21997             return;
21998         }
21999         if(typeof w == 'number'){
22000             
22001             this.iframe.style.width = w + 'px';
22002         }
22003         if(typeof h == 'number'){
22004             
22005             this.iframe.style.height = h + 'px';
22006             if(this.doc){
22007                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22008             }
22009         }
22010         
22011     },
22012
22013     /**
22014      * Toggles the editor between standard and source edit mode.
22015      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22016      */
22017     toggleSourceEdit : function(sourceEditMode){
22018         
22019         this.sourceEditMode = sourceEditMode === true;
22020         
22021         if(this.sourceEditMode){
22022  
22023             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22024             
22025         }else{
22026             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22027             //this.iframe.className = '';
22028             this.deferFocus();
22029         }
22030         //this.setSize(this.owner.wrap.getSize());
22031         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22032     },
22033
22034     
22035   
22036
22037     /**
22038      * Protected method that will not generally be called directly. If you need/want
22039      * custom HTML cleanup, this is the method you should override.
22040      * @param {String} html The HTML to be cleaned
22041      * return {String} The cleaned HTML
22042      */
22043     cleanHtml : function(html){
22044         html = String(html);
22045         if(html.length > 5){
22046             if(Roo.isSafari){ // strip safari nonsense
22047                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22048             }
22049         }
22050         if(html == '&nbsp;'){
22051             html = '';
22052         }
22053         return html;
22054     },
22055
22056     /**
22057      * HTML Editor -> Textarea
22058      * Protected method that will not generally be called directly. Syncs the contents
22059      * of the editor iframe with the textarea.
22060      */
22061     syncValue : function(){
22062         if(this.initialized){
22063             var bd = (this.doc.body || this.doc.documentElement);
22064             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22065             var html = bd.innerHTML;
22066             if(Roo.isSafari){
22067                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22068                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22069                 if(m && m[1]){
22070                     html = '<div style="'+m[0]+'">' + html + '</div>';
22071                 }
22072             }
22073             html = this.cleanHtml(html);
22074             // fix up the special chars.. normaly like back quotes in word...
22075             // however we do not want to do this with chinese..
22076             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22077                 var cc = b.charCodeAt();
22078                 if (
22079                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22080                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22081                     (cc >= 0xf900 && cc < 0xfb00 )
22082                 ) {
22083                         return b;
22084                 }
22085                 return "&#"+cc+";" 
22086             });
22087             if(this.owner.fireEvent('beforesync', this, html) !== false){
22088                 this.el.dom.value = html;
22089                 this.owner.fireEvent('sync', this, html);
22090             }
22091         }
22092     },
22093
22094     /**
22095      * Protected method that will not generally be called directly. Pushes the value of the textarea
22096      * into the iframe editor.
22097      */
22098     pushValue : function(){
22099         if(this.initialized){
22100             var v = this.el.dom.value.trim();
22101             
22102 //            if(v.length < 1){
22103 //                v = '&#160;';
22104 //            }
22105             
22106             if(this.owner.fireEvent('beforepush', this, v) !== false){
22107                 var d = (this.doc.body || this.doc.documentElement);
22108                 d.innerHTML = v;
22109                 this.cleanUpPaste();
22110                 this.el.dom.value = d.innerHTML;
22111                 this.owner.fireEvent('push', this, v);
22112             }
22113         }
22114     },
22115
22116     // private
22117     deferFocus : function(){
22118         this.focus.defer(10, this);
22119     },
22120
22121     // doc'ed in Field
22122     focus : function(){
22123         if(this.win && !this.sourceEditMode){
22124             this.win.focus();
22125         }else{
22126             this.el.focus();
22127         }
22128     },
22129     
22130     assignDocWin: function()
22131     {
22132         var iframe = this.iframe;
22133         
22134          if(Roo.isIE){
22135             this.doc = iframe.contentWindow.document;
22136             this.win = iframe.contentWindow;
22137         } else {
22138 //            if (!Roo.get(this.frameId)) {
22139 //                return;
22140 //            }
22141 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22142 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22143             
22144             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22145                 return;
22146             }
22147             
22148             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22149             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22150         }
22151     },
22152     
22153     // private
22154     initEditor : function(){
22155         //console.log("INIT EDITOR");
22156         this.assignDocWin();
22157         
22158         
22159         
22160         this.doc.designMode="on";
22161         this.doc.open();
22162         this.doc.write(this.getDocMarkup());
22163         this.doc.close();
22164         
22165         var dbody = (this.doc.body || this.doc.documentElement);
22166         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22167         // this copies styles from the containing element into thsi one..
22168         // not sure why we need all of this..
22169         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22170         
22171         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22172         //ss['background-attachment'] = 'fixed'; // w3c
22173         dbody.bgProperties = 'fixed'; // ie
22174         //Roo.DomHelper.applyStyles(dbody, ss);
22175         Roo.EventManager.on(this.doc, {
22176             //'mousedown': this.onEditorEvent,
22177             'mouseup': this.onEditorEvent,
22178             'dblclick': this.onEditorEvent,
22179             'click': this.onEditorEvent,
22180             'keyup': this.onEditorEvent,
22181             buffer:100,
22182             scope: this
22183         });
22184         if(Roo.isGecko){
22185             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22186         }
22187         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22188             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22189         }
22190         this.initialized = true;
22191
22192         this.owner.fireEvent('initialize', this);
22193         this.pushValue();
22194     },
22195
22196     // private
22197     onDestroy : function(){
22198         
22199         
22200         
22201         if(this.rendered){
22202             
22203             //for (var i =0; i < this.toolbars.length;i++) {
22204             //    // fixme - ask toolbars for heights?
22205             //    this.toolbars[i].onDestroy();
22206            // }
22207             
22208             //this.wrap.dom.innerHTML = '';
22209             //this.wrap.remove();
22210         }
22211     },
22212
22213     // private
22214     onFirstFocus : function(){
22215         
22216         this.assignDocWin();
22217         
22218         
22219         this.activated = true;
22220          
22221     
22222         if(Roo.isGecko){ // prevent silly gecko errors
22223             this.win.focus();
22224             var s = this.win.getSelection();
22225             if(!s.focusNode || s.focusNode.nodeType != 3){
22226                 var r = s.getRangeAt(0);
22227                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22228                 r.collapse(true);
22229                 this.deferFocus();
22230             }
22231             try{
22232                 this.execCmd('useCSS', true);
22233                 this.execCmd('styleWithCSS', false);
22234             }catch(e){}
22235         }
22236         this.owner.fireEvent('activate', this);
22237     },
22238
22239     // private
22240     adjustFont: function(btn){
22241         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22242         //if(Roo.isSafari){ // safari
22243         //    adjust *= 2;
22244        // }
22245         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22246         if(Roo.isSafari){ // safari
22247             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22248             v =  (v < 10) ? 10 : v;
22249             v =  (v > 48) ? 48 : v;
22250             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22251             
22252         }
22253         
22254         
22255         v = Math.max(1, v+adjust);
22256         
22257         this.execCmd('FontSize', v  );
22258     },
22259
22260     onEditorEvent : function(e)
22261     {
22262         this.owner.fireEvent('editorevent', this, e);
22263       //  this.updateToolbar();
22264         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22265     },
22266
22267     insertTag : function(tg)
22268     {
22269         // could be a bit smarter... -> wrap the current selected tRoo..
22270         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22271             
22272             range = this.createRange(this.getSelection());
22273             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22274             wrappingNode.appendChild(range.extractContents());
22275             range.insertNode(wrappingNode);
22276
22277             return;
22278             
22279             
22280             
22281         }
22282         this.execCmd("formatblock",   tg);
22283         
22284     },
22285     
22286     insertText : function(txt)
22287     {
22288         
22289         
22290         var range = this.createRange();
22291         range.deleteContents();
22292                //alert(Sender.getAttribute('label'));
22293                
22294         range.insertNode(this.doc.createTextNode(txt));
22295     } ,
22296     
22297      
22298
22299     /**
22300      * Executes a Midas editor command on the editor document and performs necessary focus and
22301      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22302      * @param {String} cmd The Midas command
22303      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22304      */
22305     relayCmd : function(cmd, value){
22306         this.win.focus();
22307         this.execCmd(cmd, value);
22308         this.owner.fireEvent('editorevent', this);
22309         //this.updateToolbar();
22310         this.owner.deferFocus();
22311     },
22312
22313     /**
22314      * Executes a Midas editor command directly on the editor document.
22315      * For visual commands, you should use {@link #relayCmd} instead.
22316      * <b>This should only be called after the editor is initialized.</b>
22317      * @param {String} cmd The Midas command
22318      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22319      */
22320     execCmd : function(cmd, value){
22321         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22322         this.syncValue();
22323     },
22324  
22325  
22326    
22327     /**
22328      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22329      * to insert tRoo.
22330      * @param {String} text | dom node.. 
22331      */
22332     insertAtCursor : function(text)
22333     {
22334         
22335         if(!this.activated){
22336             return;
22337         }
22338         /*
22339         if(Roo.isIE){
22340             this.win.focus();
22341             var r = this.doc.selection.createRange();
22342             if(r){
22343                 r.collapse(true);
22344                 r.pasteHTML(text);
22345                 this.syncValue();
22346                 this.deferFocus();
22347             
22348             }
22349             return;
22350         }
22351         */
22352         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22353             this.win.focus();
22354             
22355             
22356             // from jquery ui (MIT licenced)
22357             var range, node;
22358             var win = this.win;
22359             
22360             if (win.getSelection && win.getSelection().getRangeAt) {
22361                 range = win.getSelection().getRangeAt(0);
22362                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22363                 range.insertNode(node);
22364             } else if (win.document.selection && win.document.selection.createRange) {
22365                 // no firefox support
22366                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22367                 win.document.selection.createRange().pasteHTML(txt);
22368             } else {
22369                 // no firefox support
22370                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22371                 this.execCmd('InsertHTML', txt);
22372             } 
22373             
22374             this.syncValue();
22375             
22376             this.deferFocus();
22377         }
22378     },
22379  // private
22380     mozKeyPress : function(e){
22381         if(e.ctrlKey){
22382             var c = e.getCharCode(), cmd;
22383           
22384             if(c > 0){
22385                 c = String.fromCharCode(c).toLowerCase();
22386                 switch(c){
22387                     case 'b':
22388                         cmd = 'bold';
22389                         break;
22390                     case 'i':
22391                         cmd = 'italic';
22392                         break;
22393                     
22394                     case 'u':
22395                         cmd = 'underline';
22396                         break;
22397                     
22398                     case 'v':
22399                         this.cleanUpPaste.defer(100, this);
22400                         return;
22401                         
22402                 }
22403                 if(cmd){
22404                     this.win.focus();
22405                     this.execCmd(cmd);
22406                     this.deferFocus();
22407                     e.preventDefault();
22408                 }
22409                 
22410             }
22411         }
22412     },
22413
22414     // private
22415     fixKeys : function(){ // load time branching for fastest keydown performance
22416         if(Roo.isIE){
22417             return function(e){
22418                 var k = e.getKey(), r;
22419                 if(k == e.TAB){
22420                     e.stopEvent();
22421                     r = this.doc.selection.createRange();
22422                     if(r){
22423                         r.collapse(true);
22424                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22425                         this.deferFocus();
22426                     }
22427                     return;
22428                 }
22429                 
22430                 if(k == e.ENTER){
22431                     r = this.doc.selection.createRange();
22432                     if(r){
22433                         var target = r.parentElement();
22434                         if(!target || target.tagName.toLowerCase() != 'li'){
22435                             e.stopEvent();
22436                             r.pasteHTML('<br />');
22437                             r.collapse(false);
22438                             r.select();
22439                         }
22440                     }
22441                 }
22442                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22443                     this.cleanUpPaste.defer(100, this);
22444                     return;
22445                 }
22446                 
22447                 
22448             };
22449         }else if(Roo.isOpera){
22450             return function(e){
22451                 var k = e.getKey();
22452                 if(k == e.TAB){
22453                     e.stopEvent();
22454                     this.win.focus();
22455                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22456                     this.deferFocus();
22457                 }
22458                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22459                     this.cleanUpPaste.defer(100, this);
22460                     return;
22461                 }
22462                 
22463             };
22464         }else if(Roo.isSafari){
22465             return function(e){
22466                 var k = e.getKey();
22467                 
22468                 if(k == e.TAB){
22469                     e.stopEvent();
22470                     this.execCmd('InsertText','\t');
22471                     this.deferFocus();
22472                     return;
22473                 }
22474                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22475                     this.cleanUpPaste.defer(100, this);
22476                     return;
22477                 }
22478                 
22479              };
22480         }
22481     }(),
22482     
22483     getAllAncestors: function()
22484     {
22485         var p = this.getSelectedNode();
22486         var a = [];
22487         if (!p) {
22488             a.push(p); // push blank onto stack..
22489             p = this.getParentElement();
22490         }
22491         
22492         
22493         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22494             a.push(p);
22495             p = p.parentNode;
22496         }
22497         a.push(this.doc.body);
22498         return a;
22499     },
22500     lastSel : false,
22501     lastSelNode : false,
22502     
22503     
22504     getSelection : function() 
22505     {
22506         this.assignDocWin();
22507         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22508     },
22509     
22510     getSelectedNode: function() 
22511     {
22512         // this may only work on Gecko!!!
22513         
22514         // should we cache this!!!!
22515         
22516         
22517         
22518          
22519         var range = this.createRange(this.getSelection()).cloneRange();
22520         
22521         if (Roo.isIE) {
22522             var parent = range.parentElement();
22523             while (true) {
22524                 var testRange = range.duplicate();
22525                 testRange.moveToElementText(parent);
22526                 if (testRange.inRange(range)) {
22527                     break;
22528                 }
22529                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22530                     break;
22531                 }
22532                 parent = parent.parentElement;
22533             }
22534             return parent;
22535         }
22536         
22537         // is ancestor a text element.
22538         var ac =  range.commonAncestorContainer;
22539         if (ac.nodeType == 3) {
22540             ac = ac.parentNode;
22541         }
22542         
22543         var ar = ac.childNodes;
22544          
22545         var nodes = [];
22546         var other_nodes = [];
22547         var has_other_nodes = false;
22548         for (var i=0;i<ar.length;i++) {
22549             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22550                 continue;
22551             }
22552             // fullly contained node.
22553             
22554             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22555                 nodes.push(ar[i]);
22556                 continue;
22557             }
22558             
22559             // probably selected..
22560             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22561                 other_nodes.push(ar[i]);
22562                 continue;
22563             }
22564             // outer..
22565             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22566                 continue;
22567             }
22568             
22569             
22570             has_other_nodes = true;
22571         }
22572         if (!nodes.length && other_nodes.length) {
22573             nodes= other_nodes;
22574         }
22575         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22576             return false;
22577         }
22578         
22579         return nodes[0];
22580     },
22581     createRange: function(sel)
22582     {
22583         // this has strange effects when using with 
22584         // top toolbar - not sure if it's a great idea.
22585         //this.editor.contentWindow.focus();
22586         if (typeof sel != "undefined") {
22587             try {
22588                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22589             } catch(e) {
22590                 return this.doc.createRange();
22591             }
22592         } else {
22593             return this.doc.createRange();
22594         }
22595     },
22596     getParentElement: function()
22597     {
22598         
22599         this.assignDocWin();
22600         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22601         
22602         var range = this.createRange(sel);
22603          
22604         try {
22605             var p = range.commonAncestorContainer;
22606             while (p.nodeType == 3) { // text node
22607                 p = p.parentNode;
22608             }
22609             return p;
22610         } catch (e) {
22611             return null;
22612         }
22613     
22614     },
22615     /***
22616      *
22617      * Range intersection.. the hard stuff...
22618      *  '-1' = before
22619      *  '0' = hits..
22620      *  '1' = after.
22621      *         [ -- selected range --- ]
22622      *   [fail]                        [fail]
22623      *
22624      *    basically..
22625      *      if end is before start or  hits it. fail.
22626      *      if start is after end or hits it fail.
22627      *
22628      *   if either hits (but other is outside. - then it's not 
22629      *   
22630      *    
22631      **/
22632     
22633     
22634     // @see http://www.thismuchiknow.co.uk/?p=64.
22635     rangeIntersectsNode : function(range, node)
22636     {
22637         var nodeRange = node.ownerDocument.createRange();
22638         try {
22639             nodeRange.selectNode(node);
22640         } catch (e) {
22641             nodeRange.selectNodeContents(node);
22642         }
22643     
22644         var rangeStartRange = range.cloneRange();
22645         rangeStartRange.collapse(true);
22646     
22647         var rangeEndRange = range.cloneRange();
22648         rangeEndRange.collapse(false);
22649     
22650         var nodeStartRange = nodeRange.cloneRange();
22651         nodeStartRange.collapse(true);
22652     
22653         var nodeEndRange = nodeRange.cloneRange();
22654         nodeEndRange.collapse(false);
22655     
22656         return rangeStartRange.compareBoundaryPoints(
22657                  Range.START_TO_START, nodeEndRange) == -1 &&
22658                rangeEndRange.compareBoundaryPoints(
22659                  Range.START_TO_START, nodeStartRange) == 1;
22660         
22661          
22662     },
22663     rangeCompareNode : function(range, node)
22664     {
22665         var nodeRange = node.ownerDocument.createRange();
22666         try {
22667             nodeRange.selectNode(node);
22668         } catch (e) {
22669             nodeRange.selectNodeContents(node);
22670         }
22671         
22672         
22673         range.collapse(true);
22674     
22675         nodeRange.collapse(true);
22676      
22677         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22678         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22679          
22680         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22681         
22682         var nodeIsBefore   =  ss == 1;
22683         var nodeIsAfter    = ee == -1;
22684         
22685         if (nodeIsBefore && nodeIsAfter) {
22686             return 0; // outer
22687         }
22688         if (!nodeIsBefore && nodeIsAfter) {
22689             return 1; //right trailed.
22690         }
22691         
22692         if (nodeIsBefore && !nodeIsAfter) {
22693             return 2;  // left trailed.
22694         }
22695         // fully contined.
22696         return 3;
22697     },
22698
22699     // private? - in a new class?
22700     cleanUpPaste :  function()
22701     {
22702         // cleans up the whole document..
22703         Roo.log('cleanuppaste');
22704         
22705         this.cleanUpChildren(this.doc.body);
22706         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22707         if (clean != this.doc.body.innerHTML) {
22708             this.doc.body.innerHTML = clean;
22709         }
22710         
22711     },
22712     
22713     cleanWordChars : function(input) {// change the chars to hex code
22714         var he = Roo.HtmlEditorCore;
22715         
22716         var output = input;
22717         Roo.each(he.swapCodes, function(sw) { 
22718             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22719             
22720             output = output.replace(swapper, sw[1]);
22721         });
22722         
22723         return output;
22724     },
22725     
22726     
22727     cleanUpChildren : function (n)
22728     {
22729         if (!n.childNodes.length) {
22730             return;
22731         }
22732         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22733            this.cleanUpChild(n.childNodes[i]);
22734         }
22735     },
22736     
22737     
22738         
22739     
22740     cleanUpChild : function (node)
22741     {
22742         var ed = this;
22743         //console.log(node);
22744         if (node.nodeName == "#text") {
22745             // clean up silly Windows -- stuff?
22746             return; 
22747         }
22748         if (node.nodeName == "#comment") {
22749             node.parentNode.removeChild(node);
22750             // clean up silly Windows -- stuff?
22751             return; 
22752         }
22753         var lcname = node.tagName.toLowerCase();
22754         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22755         // whitelist of tags..
22756         
22757         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22758             // remove node.
22759             node.parentNode.removeChild(node);
22760             return;
22761             
22762         }
22763         
22764         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22765         
22766         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22767         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22768         
22769         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22770         //    remove_keep_children = true;
22771         //}
22772         
22773         if (remove_keep_children) {
22774             this.cleanUpChildren(node);
22775             // inserts everything just before this node...
22776             while (node.childNodes.length) {
22777                 var cn = node.childNodes[0];
22778                 node.removeChild(cn);
22779                 node.parentNode.insertBefore(cn, node);
22780             }
22781             node.parentNode.removeChild(node);
22782             return;
22783         }
22784         
22785         if (!node.attributes || !node.attributes.length) {
22786             this.cleanUpChildren(node);
22787             return;
22788         }
22789         
22790         function cleanAttr(n,v)
22791         {
22792             
22793             if (v.match(/^\./) || v.match(/^\//)) {
22794                 return;
22795             }
22796             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22797                 return;
22798             }
22799             if (v.match(/^#/)) {
22800                 return;
22801             }
22802 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22803             node.removeAttribute(n);
22804             
22805         }
22806         
22807         var cwhite = this.cwhite;
22808         var cblack = this.cblack;
22809             
22810         function cleanStyle(n,v)
22811         {
22812             if (v.match(/expression/)) { //XSS?? should we even bother..
22813                 node.removeAttribute(n);
22814                 return;
22815             }
22816             
22817             var parts = v.split(/;/);
22818             var clean = [];
22819             
22820             Roo.each(parts, function(p) {
22821                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22822                 if (!p.length) {
22823                     return true;
22824                 }
22825                 var l = p.split(':').shift().replace(/\s+/g,'');
22826                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22827                 
22828                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22829 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22830                     //node.removeAttribute(n);
22831                     return true;
22832                 }
22833                 //Roo.log()
22834                 // only allow 'c whitelisted system attributes'
22835                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22836 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22837                     //node.removeAttribute(n);
22838                     return true;
22839                 }
22840                 
22841                 
22842                  
22843                 
22844                 clean.push(p);
22845                 return true;
22846             });
22847             if (clean.length) { 
22848                 node.setAttribute(n, clean.join(';'));
22849             } else {
22850                 node.removeAttribute(n);
22851             }
22852             
22853         }
22854         
22855         
22856         for (var i = node.attributes.length-1; i > -1 ; i--) {
22857             var a = node.attributes[i];
22858             //console.log(a);
22859             
22860             if (a.name.toLowerCase().substr(0,2)=='on')  {
22861                 node.removeAttribute(a.name);
22862                 continue;
22863             }
22864             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22865                 node.removeAttribute(a.name);
22866                 continue;
22867             }
22868             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22869                 cleanAttr(a.name,a.value); // fixme..
22870                 continue;
22871             }
22872             if (a.name == 'style') {
22873                 cleanStyle(a.name,a.value);
22874                 continue;
22875             }
22876             /// clean up MS crap..
22877             // tecnically this should be a list of valid class'es..
22878             
22879             
22880             if (a.name == 'class') {
22881                 if (a.value.match(/^Mso/)) {
22882                     node.className = '';
22883                 }
22884                 
22885                 if (a.value.match(/^body$/)) {
22886                     node.className = '';
22887                 }
22888                 continue;
22889             }
22890             
22891             // style cleanup!?
22892             // class cleanup?
22893             
22894         }
22895         
22896         
22897         this.cleanUpChildren(node);
22898         
22899         
22900     },
22901     
22902     /**
22903      * Clean up MS wordisms...
22904      */
22905     cleanWord : function(node)
22906     {
22907         
22908         
22909         if (!node) {
22910             this.cleanWord(this.doc.body);
22911             return;
22912         }
22913         if (node.nodeName == "#text") {
22914             // clean up silly Windows -- stuff?
22915             return; 
22916         }
22917         if (node.nodeName == "#comment") {
22918             node.parentNode.removeChild(node);
22919             // clean up silly Windows -- stuff?
22920             return; 
22921         }
22922         
22923         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22924             node.parentNode.removeChild(node);
22925             return;
22926         }
22927         
22928         // remove - but keep children..
22929         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22930             while (node.childNodes.length) {
22931                 var cn = node.childNodes[0];
22932                 node.removeChild(cn);
22933                 node.parentNode.insertBefore(cn, node);
22934             }
22935             node.parentNode.removeChild(node);
22936             this.iterateChildren(node, this.cleanWord);
22937             return;
22938         }
22939         // clean styles
22940         if (node.className.length) {
22941             
22942             var cn = node.className.split(/\W+/);
22943             var cna = [];
22944             Roo.each(cn, function(cls) {
22945                 if (cls.match(/Mso[a-zA-Z]+/)) {
22946                     return;
22947                 }
22948                 cna.push(cls);
22949             });
22950             node.className = cna.length ? cna.join(' ') : '';
22951             if (!cna.length) {
22952                 node.removeAttribute("class");
22953             }
22954         }
22955         
22956         if (node.hasAttribute("lang")) {
22957             node.removeAttribute("lang");
22958         }
22959         
22960         if (node.hasAttribute("style")) {
22961             
22962             var styles = node.getAttribute("style").split(";");
22963             var nstyle = [];
22964             Roo.each(styles, function(s) {
22965                 if (!s.match(/:/)) {
22966                     return;
22967                 }
22968                 var kv = s.split(":");
22969                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22970                     return;
22971                 }
22972                 // what ever is left... we allow.
22973                 nstyle.push(s);
22974             });
22975             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22976             if (!nstyle.length) {
22977                 node.removeAttribute('style');
22978             }
22979         }
22980         this.iterateChildren(node, this.cleanWord);
22981         
22982         
22983         
22984     },
22985     /**
22986      * iterateChildren of a Node, calling fn each time, using this as the scole..
22987      * @param {DomNode} node node to iterate children of.
22988      * @param {Function} fn method of this class to call on each item.
22989      */
22990     iterateChildren : function(node, fn)
22991     {
22992         if (!node.childNodes.length) {
22993                 return;
22994         }
22995         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22996            fn.call(this, node.childNodes[i])
22997         }
22998     },
22999     
23000     
23001     /**
23002      * cleanTableWidths.
23003      *
23004      * Quite often pasting from word etc.. results in tables with column and widths.
23005      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23006      *
23007      */
23008     cleanTableWidths : function(node)
23009     {
23010          
23011          
23012         if (!node) {
23013             this.cleanTableWidths(this.doc.body);
23014             return;
23015         }
23016         
23017         // ignore list...
23018         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23019             return; 
23020         }
23021         Roo.log(node.tagName);
23022         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23023             this.iterateChildren(node, this.cleanTableWidths);
23024             return;
23025         }
23026         if (node.hasAttribute('width')) {
23027             node.removeAttribute('width');
23028         }
23029         
23030          
23031         if (node.hasAttribute("style")) {
23032             // pretty basic...
23033             
23034             var styles = node.getAttribute("style").split(";");
23035             var nstyle = [];
23036             Roo.each(styles, function(s) {
23037                 if (!s.match(/:/)) {
23038                     return;
23039                 }
23040                 var kv = s.split(":");
23041                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23042                     return;
23043                 }
23044                 // what ever is left... we allow.
23045                 nstyle.push(s);
23046             });
23047             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23048             if (!nstyle.length) {
23049                 node.removeAttribute('style');
23050             }
23051         }
23052         
23053         this.iterateChildren(node, this.cleanTableWidths);
23054         
23055         
23056     },
23057     
23058     
23059     
23060     
23061     domToHTML : function(currentElement, depth, nopadtext) {
23062         
23063         depth = depth || 0;
23064         nopadtext = nopadtext || false;
23065     
23066         if (!currentElement) {
23067             return this.domToHTML(this.doc.body);
23068         }
23069         
23070         //Roo.log(currentElement);
23071         var j;
23072         var allText = false;
23073         var nodeName = currentElement.nodeName;
23074         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23075         
23076         if  (nodeName == '#text') {
23077             
23078             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23079         }
23080         
23081         
23082         var ret = '';
23083         if (nodeName != 'BODY') {
23084              
23085             var i = 0;
23086             // Prints the node tagName, such as <A>, <IMG>, etc
23087             if (tagName) {
23088                 var attr = [];
23089                 for(i = 0; i < currentElement.attributes.length;i++) {
23090                     // quoting?
23091                     var aname = currentElement.attributes.item(i).name;
23092                     if (!currentElement.attributes.item(i).value.length) {
23093                         continue;
23094                     }
23095                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23096                 }
23097                 
23098                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23099             } 
23100             else {
23101                 
23102                 // eack
23103             }
23104         } else {
23105             tagName = false;
23106         }
23107         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23108             return ret;
23109         }
23110         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23111             nopadtext = true;
23112         }
23113         
23114         
23115         // Traverse the tree
23116         i = 0;
23117         var currentElementChild = currentElement.childNodes.item(i);
23118         var allText = true;
23119         var innerHTML  = '';
23120         lastnode = '';
23121         while (currentElementChild) {
23122             // Formatting code (indent the tree so it looks nice on the screen)
23123             var nopad = nopadtext;
23124             if (lastnode == 'SPAN') {
23125                 nopad  = true;
23126             }
23127             // text
23128             if  (currentElementChild.nodeName == '#text') {
23129                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23130                 toadd = nopadtext ? toadd : toadd.trim();
23131                 if (!nopad && toadd.length > 80) {
23132                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23133                 }
23134                 innerHTML  += toadd;
23135                 
23136                 i++;
23137                 currentElementChild = currentElement.childNodes.item(i);
23138                 lastNode = '';
23139                 continue;
23140             }
23141             allText = false;
23142             
23143             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23144                 
23145             // Recursively traverse the tree structure of the child node
23146             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23147             lastnode = currentElementChild.nodeName;
23148             i++;
23149             currentElementChild=currentElement.childNodes.item(i);
23150         }
23151         
23152         ret += innerHTML;
23153         
23154         if (!allText) {
23155                 // The remaining code is mostly for formatting the tree
23156             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23157         }
23158         
23159         
23160         if (tagName) {
23161             ret+= "</"+tagName+">";
23162         }
23163         return ret;
23164         
23165     },
23166         
23167     applyBlacklists : function()
23168     {
23169         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23170         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23171         
23172         this.white = [];
23173         this.black = [];
23174         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23175             if (b.indexOf(tag) > -1) {
23176                 return;
23177             }
23178             this.white.push(tag);
23179             
23180         }, this);
23181         
23182         Roo.each(w, function(tag) {
23183             if (b.indexOf(tag) > -1) {
23184                 return;
23185             }
23186             if (this.white.indexOf(tag) > -1) {
23187                 return;
23188             }
23189             this.white.push(tag);
23190             
23191         }, this);
23192         
23193         
23194         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23195             if (w.indexOf(tag) > -1) {
23196                 return;
23197             }
23198             this.black.push(tag);
23199             
23200         }, this);
23201         
23202         Roo.each(b, function(tag) {
23203             if (w.indexOf(tag) > -1) {
23204                 return;
23205             }
23206             if (this.black.indexOf(tag) > -1) {
23207                 return;
23208             }
23209             this.black.push(tag);
23210             
23211         }, this);
23212         
23213         
23214         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23215         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23216         
23217         this.cwhite = [];
23218         this.cblack = [];
23219         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23220             if (b.indexOf(tag) > -1) {
23221                 return;
23222             }
23223             this.cwhite.push(tag);
23224             
23225         }, this);
23226         
23227         Roo.each(w, function(tag) {
23228             if (b.indexOf(tag) > -1) {
23229                 return;
23230             }
23231             if (this.cwhite.indexOf(tag) > -1) {
23232                 return;
23233             }
23234             this.cwhite.push(tag);
23235             
23236         }, this);
23237         
23238         
23239         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23240             if (w.indexOf(tag) > -1) {
23241                 return;
23242             }
23243             this.cblack.push(tag);
23244             
23245         }, this);
23246         
23247         Roo.each(b, function(tag) {
23248             if (w.indexOf(tag) > -1) {
23249                 return;
23250             }
23251             if (this.cblack.indexOf(tag) > -1) {
23252                 return;
23253             }
23254             this.cblack.push(tag);
23255             
23256         }, this);
23257     },
23258     
23259     setStylesheets : function(stylesheets)
23260     {
23261         if(typeof(stylesheets) == 'string'){
23262             Roo.get(this.iframe.contentDocument.head).createChild({
23263                 tag : 'link',
23264                 rel : 'stylesheet',
23265                 type : 'text/css',
23266                 href : stylesheets
23267             });
23268             
23269             return;
23270         }
23271         var _this = this;
23272      
23273         Roo.each(stylesheets, function(s) {
23274             if(!s.length){
23275                 return;
23276             }
23277             
23278             Roo.get(_this.iframe.contentDocument.head).createChild({
23279                 tag : 'link',
23280                 rel : 'stylesheet',
23281                 type : 'text/css',
23282                 href : s
23283             });
23284         });
23285
23286         
23287     },
23288     
23289     removeStylesheets : function()
23290     {
23291         var _this = this;
23292         
23293         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23294             s.remove();
23295         });
23296     },
23297     
23298     setStyle : function(style)
23299     {
23300         Roo.get(this.iframe.contentDocument.head).createChild({
23301             tag : 'style',
23302             type : 'text/css',
23303             html : style
23304         });
23305
23306         return;
23307     }
23308     
23309     // hide stuff that is not compatible
23310     /**
23311      * @event blur
23312      * @hide
23313      */
23314     /**
23315      * @event change
23316      * @hide
23317      */
23318     /**
23319      * @event focus
23320      * @hide
23321      */
23322     /**
23323      * @event specialkey
23324      * @hide
23325      */
23326     /**
23327      * @cfg {String} fieldClass @hide
23328      */
23329     /**
23330      * @cfg {String} focusClass @hide
23331      */
23332     /**
23333      * @cfg {String} autoCreate @hide
23334      */
23335     /**
23336      * @cfg {String} inputType @hide
23337      */
23338     /**
23339      * @cfg {String} invalidClass @hide
23340      */
23341     /**
23342      * @cfg {String} invalidText @hide
23343      */
23344     /**
23345      * @cfg {String} msgFx @hide
23346      */
23347     /**
23348      * @cfg {String} validateOnBlur @hide
23349      */
23350 });
23351
23352 Roo.HtmlEditorCore.white = [
23353         'area', 'br', 'img', 'input', 'hr', 'wbr',
23354         
23355        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23356        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23357        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23358        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23359        'table',   'ul',         'xmp', 
23360        
23361        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23362       'thead',   'tr', 
23363      
23364       'dir', 'menu', 'ol', 'ul', 'dl',
23365        
23366       'embed',  'object'
23367 ];
23368
23369
23370 Roo.HtmlEditorCore.black = [
23371     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23372         'applet', // 
23373         'base',   'basefont', 'bgsound', 'blink',  'body', 
23374         'frame',  'frameset', 'head',    'html',   'ilayer', 
23375         'iframe', 'layer',  'link',     'meta',    'object',   
23376         'script', 'style' ,'title',  'xml' // clean later..
23377 ];
23378 Roo.HtmlEditorCore.clean = [
23379     'script', 'style', 'title', 'xml'
23380 ];
23381 Roo.HtmlEditorCore.remove = [
23382     'font'
23383 ];
23384 // attributes..
23385
23386 Roo.HtmlEditorCore.ablack = [
23387     'on'
23388 ];
23389     
23390 Roo.HtmlEditorCore.aclean = [ 
23391     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23392 ];
23393
23394 // protocols..
23395 Roo.HtmlEditorCore.pwhite= [
23396         'http',  'https',  'mailto'
23397 ];
23398
23399 // white listed style attributes.
23400 Roo.HtmlEditorCore.cwhite= [
23401       //  'text-align', /// default is to allow most things..
23402       
23403          
23404 //        'font-size'//??
23405 ];
23406
23407 // black listed style attributes.
23408 Roo.HtmlEditorCore.cblack= [
23409       //  'font-size' -- this can be set by the project 
23410 ];
23411
23412
23413 Roo.HtmlEditorCore.swapCodes   =[ 
23414     [    8211, "--" ], 
23415     [    8212, "--" ], 
23416     [    8216,  "'" ],  
23417     [    8217, "'" ],  
23418     [    8220, '"' ],  
23419     [    8221, '"' ],  
23420     [    8226, "*" ],  
23421     [    8230, "..." ]
23422 ]; 
23423
23424     /*
23425  * - LGPL
23426  *
23427  * HtmlEditor
23428  * 
23429  */
23430
23431 /**
23432  * @class Roo.bootstrap.HtmlEditor
23433  * @extends Roo.bootstrap.TextArea
23434  * Bootstrap HtmlEditor class
23435
23436  * @constructor
23437  * Create a new HtmlEditor
23438  * @param {Object} config The config object
23439  */
23440
23441 Roo.bootstrap.HtmlEditor = function(config){
23442     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23443     if (!this.toolbars) {
23444         this.toolbars = [];
23445     }
23446     
23447     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23448     this.addEvents({
23449             /**
23450              * @event initialize
23451              * Fires when the editor is fully initialized (including the iframe)
23452              * @param {HtmlEditor} this
23453              */
23454             initialize: true,
23455             /**
23456              * @event activate
23457              * Fires when the editor is first receives the focus. Any insertion must wait
23458              * until after this event.
23459              * @param {HtmlEditor} this
23460              */
23461             activate: true,
23462              /**
23463              * @event beforesync
23464              * Fires before the textarea is updated with content from the editor iframe. Return false
23465              * to cancel the sync.
23466              * @param {HtmlEditor} this
23467              * @param {String} html
23468              */
23469             beforesync: true,
23470              /**
23471              * @event beforepush
23472              * Fires before the iframe editor is updated with content from the textarea. Return false
23473              * to cancel the push.
23474              * @param {HtmlEditor} this
23475              * @param {String} html
23476              */
23477             beforepush: true,
23478              /**
23479              * @event sync
23480              * Fires when the textarea is updated with content from the editor iframe.
23481              * @param {HtmlEditor} this
23482              * @param {String} html
23483              */
23484             sync: true,
23485              /**
23486              * @event push
23487              * Fires when the iframe editor is updated with content from the textarea.
23488              * @param {HtmlEditor} this
23489              * @param {String} html
23490              */
23491             push: true,
23492              /**
23493              * @event editmodechange
23494              * Fires when the editor switches edit modes
23495              * @param {HtmlEditor} this
23496              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23497              */
23498             editmodechange: true,
23499             /**
23500              * @event editorevent
23501              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23502              * @param {HtmlEditor} this
23503              */
23504             editorevent: true,
23505             /**
23506              * @event firstfocus
23507              * Fires when on first focus - needed by toolbars..
23508              * @param {HtmlEditor} this
23509              */
23510             firstfocus: true,
23511             /**
23512              * @event autosave
23513              * Auto save the htmlEditor value as a file into Events
23514              * @param {HtmlEditor} this
23515              */
23516             autosave: true,
23517             /**
23518              * @event savedpreview
23519              * preview the saved version of htmlEditor
23520              * @param {HtmlEditor} this
23521              */
23522             savedpreview: true
23523         });
23524 };
23525
23526
23527 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23528     
23529     
23530       /**
23531      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23532      */
23533     toolbars : false,
23534     
23535      /**
23536     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23537     */
23538     btns : [],
23539    
23540      /**
23541      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23542      *                        Roo.resizable.
23543      */
23544     resizable : false,
23545      /**
23546      * @cfg {Number} height (in pixels)
23547      */   
23548     height: 300,
23549    /**
23550      * @cfg {Number} width (in pixels)
23551      */   
23552     width: false,
23553     
23554     /**
23555      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23556      * 
23557      */
23558     stylesheets: false,
23559     
23560     // id of frame..
23561     frameId: false,
23562     
23563     // private properties
23564     validationEvent : false,
23565     deferHeight: true,
23566     initialized : false,
23567     activated : false,
23568     
23569     onFocus : Roo.emptyFn,
23570     iframePad:3,
23571     hideMode:'offsets',
23572     
23573     tbContainer : false,
23574     
23575     bodyCls : '',
23576     
23577     toolbarContainer :function() {
23578         return this.wrap.select('.x-html-editor-tb',true).first();
23579     },
23580
23581     /**
23582      * Protected method that will not generally be called directly. It
23583      * is called when the editor creates its toolbar. Override this method if you need to
23584      * add custom toolbar buttons.
23585      * @param {HtmlEditor} editor
23586      */
23587     createToolbar : function(){
23588         Roo.log('renewing');
23589         Roo.log("create toolbars");
23590         
23591         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23592         this.toolbars[0].render(this.toolbarContainer());
23593         
23594         return;
23595         
23596 //        if (!editor.toolbars || !editor.toolbars.length) {
23597 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23598 //        }
23599 //        
23600 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23601 //            editor.toolbars[i] = Roo.factory(
23602 //                    typeof(editor.toolbars[i]) == 'string' ?
23603 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23604 //                Roo.bootstrap.HtmlEditor);
23605 //            editor.toolbars[i].init(editor);
23606 //        }
23607     },
23608
23609      
23610     // private
23611     onRender : function(ct, position)
23612     {
23613        // Roo.log("Call onRender: " + this.xtype);
23614         var _t = this;
23615         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23616       
23617         this.wrap = this.inputEl().wrap({
23618             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23619         });
23620         
23621         this.editorcore.onRender(ct, position);
23622          
23623         if (this.resizable) {
23624             this.resizeEl = new Roo.Resizable(this.wrap, {
23625                 pinned : true,
23626                 wrap: true,
23627                 dynamic : true,
23628                 minHeight : this.height,
23629                 height: this.height,
23630                 handles : this.resizable,
23631                 width: this.width,
23632                 listeners : {
23633                     resize : function(r, w, h) {
23634                         _t.onResize(w,h); // -something
23635                     }
23636                 }
23637             });
23638             
23639         }
23640         this.createToolbar(this);
23641        
23642         
23643         if(!this.width && this.resizable){
23644             this.setSize(this.wrap.getSize());
23645         }
23646         if (this.resizeEl) {
23647             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23648             // should trigger onReize..
23649         }
23650         
23651     },
23652
23653     // private
23654     onResize : function(w, h)
23655     {
23656         Roo.log('resize: ' +w + ',' + h );
23657         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23658         var ew = false;
23659         var eh = false;
23660         
23661         if(this.inputEl() ){
23662             if(typeof w == 'number'){
23663                 var aw = w - this.wrap.getFrameWidth('lr');
23664                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23665                 ew = aw;
23666             }
23667             if(typeof h == 'number'){
23668                  var tbh = -11;  // fixme it needs to tool bar size!
23669                 for (var i =0; i < this.toolbars.length;i++) {
23670                     // fixme - ask toolbars for heights?
23671                     tbh += this.toolbars[i].el.getHeight();
23672                     //if (this.toolbars[i].footer) {
23673                     //    tbh += this.toolbars[i].footer.el.getHeight();
23674                     //}
23675                 }
23676               
23677                 
23678                 
23679                 
23680                 
23681                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23682                 ah -= 5; // knock a few pixes off for look..
23683                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23684                 var eh = ah;
23685             }
23686         }
23687         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23688         this.editorcore.onResize(ew,eh);
23689         
23690     },
23691
23692     /**
23693      * Toggles the editor between standard and source edit mode.
23694      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23695      */
23696     toggleSourceEdit : function(sourceEditMode)
23697     {
23698         this.editorcore.toggleSourceEdit(sourceEditMode);
23699         
23700         if(this.editorcore.sourceEditMode){
23701             Roo.log('editor - showing textarea');
23702             
23703 //            Roo.log('in');
23704 //            Roo.log(this.syncValue());
23705             this.syncValue();
23706             this.inputEl().removeClass(['hide', 'x-hidden']);
23707             this.inputEl().dom.removeAttribute('tabIndex');
23708             this.inputEl().focus();
23709         }else{
23710             Roo.log('editor - hiding textarea');
23711 //            Roo.log('out')
23712 //            Roo.log(this.pushValue()); 
23713             this.pushValue();
23714             
23715             this.inputEl().addClass(['hide', 'x-hidden']);
23716             this.inputEl().dom.setAttribute('tabIndex', -1);
23717             //this.deferFocus();
23718         }
23719          
23720         if(this.resizable){
23721             this.setSize(this.wrap.getSize());
23722         }
23723         
23724         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23725     },
23726  
23727     // private (for BoxComponent)
23728     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23729
23730     // private (for BoxComponent)
23731     getResizeEl : function(){
23732         return this.wrap;
23733     },
23734
23735     // private (for BoxComponent)
23736     getPositionEl : function(){
23737         return this.wrap;
23738     },
23739
23740     // private
23741     initEvents : function(){
23742         this.originalValue = this.getValue();
23743     },
23744
23745 //    /**
23746 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23747 //     * @method
23748 //     */
23749 //    markInvalid : Roo.emptyFn,
23750 //    /**
23751 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23752 //     * @method
23753 //     */
23754 //    clearInvalid : Roo.emptyFn,
23755
23756     setValue : function(v){
23757         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23758         this.editorcore.pushValue();
23759     },
23760
23761      
23762     // private
23763     deferFocus : function(){
23764         this.focus.defer(10, this);
23765     },
23766
23767     // doc'ed in Field
23768     focus : function(){
23769         this.editorcore.focus();
23770         
23771     },
23772       
23773
23774     // private
23775     onDestroy : function(){
23776         
23777         
23778         
23779         if(this.rendered){
23780             
23781             for (var i =0; i < this.toolbars.length;i++) {
23782                 // fixme - ask toolbars for heights?
23783                 this.toolbars[i].onDestroy();
23784             }
23785             
23786             this.wrap.dom.innerHTML = '';
23787             this.wrap.remove();
23788         }
23789     },
23790
23791     // private
23792     onFirstFocus : function(){
23793         //Roo.log("onFirstFocus");
23794         this.editorcore.onFirstFocus();
23795          for (var i =0; i < this.toolbars.length;i++) {
23796             this.toolbars[i].onFirstFocus();
23797         }
23798         
23799     },
23800     
23801     // private
23802     syncValue : function()
23803     {   
23804         this.editorcore.syncValue();
23805     },
23806     
23807     pushValue : function()
23808     {   
23809         this.editorcore.pushValue();
23810     }
23811      
23812     
23813     // hide stuff that is not compatible
23814     /**
23815      * @event blur
23816      * @hide
23817      */
23818     /**
23819      * @event change
23820      * @hide
23821      */
23822     /**
23823      * @event focus
23824      * @hide
23825      */
23826     /**
23827      * @event specialkey
23828      * @hide
23829      */
23830     /**
23831      * @cfg {String} fieldClass @hide
23832      */
23833     /**
23834      * @cfg {String} focusClass @hide
23835      */
23836     /**
23837      * @cfg {String} autoCreate @hide
23838      */
23839     /**
23840      * @cfg {String} inputType @hide
23841      */
23842     /**
23843      * @cfg {String} invalidClass @hide
23844      */
23845     /**
23846      * @cfg {String} invalidText @hide
23847      */
23848     /**
23849      * @cfg {String} msgFx @hide
23850      */
23851     /**
23852      * @cfg {String} validateOnBlur @hide
23853      */
23854 });
23855  
23856     
23857    
23858    
23859    
23860       
23861 Roo.namespace('Roo.bootstrap.htmleditor');
23862 /**
23863  * @class Roo.bootstrap.HtmlEditorToolbar1
23864  * Basic Toolbar
23865  * 
23866  * Usage:
23867  *
23868  new Roo.bootstrap.HtmlEditor({
23869     ....
23870     toolbars : [
23871         new Roo.bootstrap.HtmlEditorToolbar1({
23872             disable : { fonts: 1 , format: 1, ..., ... , ...],
23873             btns : [ .... ]
23874         })
23875     }
23876      
23877  * 
23878  * @cfg {Object} disable List of elements to disable..
23879  * @cfg {Array} btns List of additional buttons.
23880  * 
23881  * 
23882  * NEEDS Extra CSS? 
23883  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23884  */
23885  
23886 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23887 {
23888     
23889     Roo.apply(this, config);
23890     
23891     // default disabled, based on 'good practice'..
23892     this.disable = this.disable || {};
23893     Roo.applyIf(this.disable, {
23894         fontSize : true,
23895         colors : true,
23896         specialElements : true
23897     });
23898     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23899     
23900     this.editor = config.editor;
23901     this.editorcore = config.editor.editorcore;
23902     
23903     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23904     
23905     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23906     // dont call parent... till later.
23907 }
23908 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23909      
23910     bar : true,
23911     
23912     editor : false,
23913     editorcore : false,
23914     
23915     
23916     formats : [
23917         "p" ,  
23918         "h1","h2","h3","h4","h5","h6", 
23919         "pre", "code", 
23920         "abbr", "acronym", "address", "cite", "samp", "var",
23921         'div','span'
23922     ],
23923     
23924     onRender : function(ct, position)
23925     {
23926        // Roo.log("Call onRender: " + this.xtype);
23927         
23928        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23929        Roo.log(this.el);
23930        this.el.dom.style.marginBottom = '0';
23931        var _this = this;
23932        var editorcore = this.editorcore;
23933        var editor= this.editor;
23934        
23935        var children = [];
23936        var btn = function(id,cmd , toggle, handler, html){
23937        
23938             var  event = toggle ? 'toggle' : 'click';
23939        
23940             var a = {
23941                 size : 'sm',
23942                 xtype: 'Button',
23943                 xns: Roo.bootstrap,
23944                 glyphicon : id,
23945                 cmd : id || cmd,
23946                 enableToggle:toggle !== false,
23947                 html : html || '',
23948                 pressed : toggle ? false : null,
23949                 listeners : {}
23950             };
23951             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23952                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23953             };
23954             children.push(a);
23955             return a;
23956        }
23957        
23958     //    var cb_box = function...
23959         
23960         var style = {
23961                 xtype: 'Button',
23962                 size : 'sm',
23963                 xns: Roo.bootstrap,
23964                 glyphicon : 'font',
23965                 //html : 'submit'
23966                 menu : {
23967                     xtype: 'Menu',
23968                     xns: Roo.bootstrap,
23969                     items:  []
23970                 }
23971         };
23972         Roo.each(this.formats, function(f) {
23973             style.menu.items.push({
23974                 xtype :'MenuItem',
23975                 xns: Roo.bootstrap,
23976                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23977                 tagname : f,
23978                 listeners : {
23979                     click : function()
23980                     {
23981                         editorcore.insertTag(this.tagname);
23982                         editor.focus();
23983                     }
23984                 }
23985                 
23986             });
23987         });
23988         children.push(style);   
23989         
23990         btn('bold',false,true);
23991         btn('italic',false,true);
23992         btn('align-left', 'justifyleft',true);
23993         btn('align-center', 'justifycenter',true);
23994         btn('align-right' , 'justifyright',true);
23995         btn('link', false, false, function(btn) {
23996             //Roo.log("create link?");
23997             var url = prompt(this.createLinkText, this.defaultLinkValue);
23998             if(url && url != 'http:/'+'/'){
23999                 this.editorcore.relayCmd('createlink', url);
24000             }
24001         }),
24002         btn('list','insertunorderedlist',true);
24003         btn('pencil', false,true, function(btn){
24004                 Roo.log(this);
24005                 this.toggleSourceEdit(btn.pressed);
24006         });
24007         
24008         if (this.editor.btns.length > 0) {
24009             for (var i = 0; i<this.editor.btns.length; i++) {
24010                 children.push(this.editor.btns[i]);
24011             }
24012         }
24013         
24014         /*
24015         var cog = {
24016                 xtype: 'Button',
24017                 size : 'sm',
24018                 xns: Roo.bootstrap,
24019                 glyphicon : 'cog',
24020                 //html : 'submit'
24021                 menu : {
24022                     xtype: 'Menu',
24023                     xns: Roo.bootstrap,
24024                     items:  []
24025                 }
24026         };
24027         
24028         cog.menu.items.push({
24029             xtype :'MenuItem',
24030             xns: Roo.bootstrap,
24031             html : Clean styles,
24032             tagname : f,
24033             listeners : {
24034                 click : function()
24035                 {
24036                     editorcore.insertTag(this.tagname);
24037                     editor.focus();
24038                 }
24039             }
24040             
24041         });
24042        */
24043         
24044          
24045        this.xtype = 'NavSimplebar';
24046         
24047         for(var i=0;i< children.length;i++) {
24048             
24049             this.buttons.add(this.addxtypeChild(children[i]));
24050             
24051         }
24052         
24053         editor.on('editorevent', this.updateToolbar, this);
24054     },
24055     onBtnClick : function(id)
24056     {
24057        this.editorcore.relayCmd(id);
24058        this.editorcore.focus();
24059     },
24060     
24061     /**
24062      * Protected method that will not generally be called directly. It triggers
24063      * a toolbar update by reading the markup state of the current selection in the editor.
24064      */
24065     updateToolbar: function(){
24066
24067         if(!this.editorcore.activated){
24068             this.editor.onFirstFocus(); // is this neeed?
24069             return;
24070         }
24071
24072         var btns = this.buttons; 
24073         var doc = this.editorcore.doc;
24074         btns.get('bold').setActive(doc.queryCommandState('bold'));
24075         btns.get('italic').setActive(doc.queryCommandState('italic'));
24076         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24077         
24078         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24079         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24080         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24081         
24082         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24083         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24084          /*
24085         
24086         var ans = this.editorcore.getAllAncestors();
24087         if (this.formatCombo) {
24088             
24089             
24090             var store = this.formatCombo.store;
24091             this.formatCombo.setValue("");
24092             for (var i =0; i < ans.length;i++) {
24093                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24094                     // select it..
24095                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24096                     break;
24097                 }
24098             }
24099         }
24100         
24101         
24102         
24103         // hides menus... - so this cant be on a menu...
24104         Roo.bootstrap.MenuMgr.hideAll();
24105         */
24106         Roo.bootstrap.MenuMgr.hideAll();
24107         //this.editorsyncValue();
24108     },
24109     onFirstFocus: function() {
24110         this.buttons.each(function(item){
24111            item.enable();
24112         });
24113     },
24114     toggleSourceEdit : function(sourceEditMode){
24115         
24116           
24117         if(sourceEditMode){
24118             Roo.log("disabling buttons");
24119            this.buttons.each( function(item){
24120                 if(item.cmd != 'pencil'){
24121                     item.disable();
24122                 }
24123             });
24124           
24125         }else{
24126             Roo.log("enabling buttons");
24127             if(this.editorcore.initialized){
24128                 this.buttons.each( function(item){
24129                     item.enable();
24130                 });
24131             }
24132             
24133         }
24134         Roo.log("calling toggole on editor");
24135         // tell the editor that it's been pressed..
24136         this.editor.toggleSourceEdit(sourceEditMode);
24137        
24138     }
24139 });
24140
24141
24142
24143
24144
24145 /**
24146  * @class Roo.bootstrap.Table.AbstractSelectionModel
24147  * @extends Roo.util.Observable
24148  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24149  * implemented by descendant classes.  This class should not be directly instantiated.
24150  * @constructor
24151  */
24152 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24153     this.locked = false;
24154     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24155 };
24156
24157
24158 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24159     /** @ignore Called by the grid automatically. Do not call directly. */
24160     init : function(grid){
24161         this.grid = grid;
24162         this.initEvents();
24163     },
24164
24165     /**
24166      * Locks the selections.
24167      */
24168     lock : function(){
24169         this.locked = true;
24170     },
24171
24172     /**
24173      * Unlocks the selections.
24174      */
24175     unlock : function(){
24176         this.locked = false;
24177     },
24178
24179     /**
24180      * Returns true if the selections are locked.
24181      * @return {Boolean}
24182      */
24183     isLocked : function(){
24184         return this.locked;
24185     }
24186 });
24187 /**
24188  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24189  * @class Roo.bootstrap.Table.RowSelectionModel
24190  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24191  * It supports multiple selections and keyboard selection/navigation. 
24192  * @constructor
24193  * @param {Object} config
24194  */
24195
24196 Roo.bootstrap.Table.RowSelectionModel = function(config){
24197     Roo.apply(this, config);
24198     this.selections = new Roo.util.MixedCollection(false, function(o){
24199         return o.id;
24200     });
24201
24202     this.last = false;
24203     this.lastActive = false;
24204
24205     this.addEvents({
24206         /**
24207              * @event selectionchange
24208              * Fires when the selection changes
24209              * @param {SelectionModel} this
24210              */
24211             "selectionchange" : true,
24212         /**
24213              * @event afterselectionchange
24214              * Fires after the selection changes (eg. by key press or clicking)
24215              * @param {SelectionModel} this
24216              */
24217             "afterselectionchange" : true,
24218         /**
24219              * @event beforerowselect
24220              * Fires when a row is selected being selected, return false to cancel.
24221              * @param {SelectionModel} this
24222              * @param {Number} rowIndex The selected index
24223              * @param {Boolean} keepExisting False if other selections will be cleared
24224              */
24225             "beforerowselect" : true,
24226         /**
24227              * @event rowselect
24228              * Fires when a row is selected.
24229              * @param {SelectionModel} this
24230              * @param {Number} rowIndex The selected index
24231              * @param {Roo.data.Record} r The record
24232              */
24233             "rowselect" : true,
24234         /**
24235              * @event rowdeselect
24236              * Fires when a row is deselected.
24237              * @param {SelectionModel} this
24238              * @param {Number} rowIndex The selected index
24239              */
24240         "rowdeselect" : true
24241     });
24242     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24243     this.locked = false;
24244  };
24245
24246 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24247     /**
24248      * @cfg {Boolean} singleSelect
24249      * True to allow selection of only one row at a time (defaults to false)
24250      */
24251     singleSelect : false,
24252
24253     // private
24254     initEvents : function()
24255     {
24256
24257         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24258         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24259         //}else{ // allow click to work like normal
24260          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24261         //}
24262         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24263         this.grid.on("rowclick", this.handleMouseDown, this);
24264         
24265         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24266             "up" : function(e){
24267                 if(!e.shiftKey){
24268                     this.selectPrevious(e.shiftKey);
24269                 }else if(this.last !== false && this.lastActive !== false){
24270                     var last = this.last;
24271                     this.selectRange(this.last,  this.lastActive-1);
24272                     this.grid.getView().focusRow(this.lastActive);
24273                     if(last !== false){
24274                         this.last = last;
24275                     }
24276                 }else{
24277                     this.selectFirstRow();
24278                 }
24279                 this.fireEvent("afterselectionchange", this);
24280             },
24281             "down" : function(e){
24282                 if(!e.shiftKey){
24283                     this.selectNext(e.shiftKey);
24284                 }else if(this.last !== false && this.lastActive !== false){
24285                     var last = this.last;
24286                     this.selectRange(this.last,  this.lastActive+1);
24287                     this.grid.getView().focusRow(this.lastActive);
24288                     if(last !== false){
24289                         this.last = last;
24290                     }
24291                 }else{
24292                     this.selectFirstRow();
24293                 }
24294                 this.fireEvent("afterselectionchange", this);
24295             },
24296             scope: this
24297         });
24298         this.grid.store.on('load', function(){
24299             this.selections.clear();
24300         },this);
24301         /*
24302         var view = this.grid.view;
24303         view.on("refresh", this.onRefresh, this);
24304         view.on("rowupdated", this.onRowUpdated, this);
24305         view.on("rowremoved", this.onRemove, this);
24306         */
24307     },
24308
24309     // private
24310     onRefresh : function()
24311     {
24312         var ds = this.grid.store, i, v = this.grid.view;
24313         var s = this.selections;
24314         s.each(function(r){
24315             if((i = ds.indexOfId(r.id)) != -1){
24316                 v.onRowSelect(i);
24317             }else{
24318                 s.remove(r);
24319             }
24320         });
24321     },
24322
24323     // private
24324     onRemove : function(v, index, r){
24325         this.selections.remove(r);
24326     },
24327
24328     // private
24329     onRowUpdated : function(v, index, r){
24330         if(this.isSelected(r)){
24331             v.onRowSelect(index);
24332         }
24333     },
24334
24335     /**
24336      * Select records.
24337      * @param {Array} records The records to select
24338      * @param {Boolean} keepExisting (optional) True to keep existing selections
24339      */
24340     selectRecords : function(records, keepExisting)
24341     {
24342         if(!keepExisting){
24343             this.clearSelections();
24344         }
24345             var ds = this.grid.store;
24346         for(var i = 0, len = records.length; i < len; i++){
24347             this.selectRow(ds.indexOf(records[i]), true);
24348         }
24349     },
24350
24351     /**
24352      * Gets the number of selected rows.
24353      * @return {Number}
24354      */
24355     getCount : function(){
24356         return this.selections.length;
24357     },
24358
24359     /**
24360      * Selects the first row in the grid.
24361      */
24362     selectFirstRow : function(){
24363         this.selectRow(0);
24364     },
24365
24366     /**
24367      * Select the last row.
24368      * @param {Boolean} keepExisting (optional) True to keep existing selections
24369      */
24370     selectLastRow : function(keepExisting){
24371         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24372         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24373     },
24374
24375     /**
24376      * Selects the row immediately following the last selected row.
24377      * @param {Boolean} keepExisting (optional) True to keep existing selections
24378      */
24379     selectNext : function(keepExisting)
24380     {
24381             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24382             this.selectRow(this.last+1, keepExisting);
24383             this.grid.getView().focusRow(this.last);
24384         }
24385     },
24386
24387     /**
24388      * Selects the row that precedes the last selected row.
24389      * @param {Boolean} keepExisting (optional) True to keep existing selections
24390      */
24391     selectPrevious : function(keepExisting){
24392         if(this.last){
24393             this.selectRow(this.last-1, keepExisting);
24394             this.grid.getView().focusRow(this.last);
24395         }
24396     },
24397
24398     /**
24399      * Returns the selected records
24400      * @return {Array} Array of selected records
24401      */
24402     getSelections : function(){
24403         return [].concat(this.selections.items);
24404     },
24405
24406     /**
24407      * Returns the first selected record.
24408      * @return {Record}
24409      */
24410     getSelected : function(){
24411         return this.selections.itemAt(0);
24412     },
24413
24414
24415     /**
24416      * Clears all selections.
24417      */
24418     clearSelections : function(fast)
24419     {
24420         if(this.locked) {
24421             return;
24422         }
24423         if(fast !== true){
24424                 var ds = this.grid.store;
24425             var s = this.selections;
24426             s.each(function(r){
24427                 this.deselectRow(ds.indexOfId(r.id));
24428             }, this);
24429             s.clear();
24430         }else{
24431             this.selections.clear();
24432         }
24433         this.last = false;
24434     },
24435
24436
24437     /**
24438      * Selects all rows.
24439      */
24440     selectAll : function(){
24441         if(this.locked) {
24442             return;
24443         }
24444         this.selections.clear();
24445         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24446             this.selectRow(i, true);
24447         }
24448     },
24449
24450     /**
24451      * Returns True if there is a selection.
24452      * @return {Boolean}
24453      */
24454     hasSelection : function(){
24455         return this.selections.length > 0;
24456     },
24457
24458     /**
24459      * Returns True if the specified row is selected.
24460      * @param {Number/Record} record The record or index of the record to check
24461      * @return {Boolean}
24462      */
24463     isSelected : function(index){
24464             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24465         return (r && this.selections.key(r.id) ? true : false);
24466     },
24467
24468     /**
24469      * Returns True if the specified record id is selected.
24470      * @param {String} id The id of record to check
24471      * @return {Boolean}
24472      */
24473     isIdSelected : function(id){
24474         return (this.selections.key(id) ? true : false);
24475     },
24476
24477
24478     // private
24479     handleMouseDBClick : function(e, t){
24480         
24481     },
24482     // private
24483     handleMouseDown : function(e, t)
24484     {
24485             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24486         if(this.isLocked() || rowIndex < 0 ){
24487             return;
24488         };
24489         if(e.shiftKey && this.last !== false){
24490             var last = this.last;
24491             this.selectRange(last, rowIndex, e.ctrlKey);
24492             this.last = last; // reset the last
24493             t.focus();
24494     
24495         }else{
24496             var isSelected = this.isSelected(rowIndex);
24497             //Roo.log("select row:" + rowIndex);
24498             if(isSelected){
24499                 this.deselectRow(rowIndex);
24500             } else {
24501                         this.selectRow(rowIndex, true);
24502             }
24503     
24504             /*
24505                 if(e.button !== 0 && isSelected){
24506                 alert('rowIndex 2: ' + rowIndex);
24507                     view.focusRow(rowIndex);
24508                 }else if(e.ctrlKey && isSelected){
24509                     this.deselectRow(rowIndex);
24510                 }else if(!isSelected){
24511                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24512                     view.focusRow(rowIndex);
24513                 }
24514             */
24515         }
24516         this.fireEvent("afterselectionchange", this);
24517     },
24518     // private
24519     handleDragableRowClick :  function(grid, rowIndex, e) 
24520     {
24521         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24522             this.selectRow(rowIndex, false);
24523             grid.view.focusRow(rowIndex);
24524              this.fireEvent("afterselectionchange", this);
24525         }
24526     },
24527     
24528     /**
24529      * Selects multiple rows.
24530      * @param {Array} rows Array of the indexes of the row to select
24531      * @param {Boolean} keepExisting (optional) True to keep existing selections
24532      */
24533     selectRows : function(rows, keepExisting){
24534         if(!keepExisting){
24535             this.clearSelections();
24536         }
24537         for(var i = 0, len = rows.length; i < len; i++){
24538             this.selectRow(rows[i], true);
24539         }
24540     },
24541
24542     /**
24543      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24544      * @param {Number} startRow The index of the first row in the range
24545      * @param {Number} endRow The index of the last row in the range
24546      * @param {Boolean} keepExisting (optional) True to retain existing selections
24547      */
24548     selectRange : function(startRow, endRow, keepExisting){
24549         if(this.locked) {
24550             return;
24551         }
24552         if(!keepExisting){
24553             this.clearSelections();
24554         }
24555         if(startRow <= endRow){
24556             for(var i = startRow; i <= endRow; i++){
24557                 this.selectRow(i, true);
24558             }
24559         }else{
24560             for(var i = startRow; i >= endRow; i--){
24561                 this.selectRow(i, true);
24562             }
24563         }
24564     },
24565
24566     /**
24567      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24568      * @param {Number} startRow The index of the first row in the range
24569      * @param {Number} endRow The index of the last row in the range
24570      */
24571     deselectRange : function(startRow, endRow, preventViewNotify){
24572         if(this.locked) {
24573             return;
24574         }
24575         for(var i = startRow; i <= endRow; i++){
24576             this.deselectRow(i, preventViewNotify);
24577         }
24578     },
24579
24580     /**
24581      * Selects a row.
24582      * @param {Number} row The index of the row to select
24583      * @param {Boolean} keepExisting (optional) True to keep existing selections
24584      */
24585     selectRow : function(index, keepExisting, preventViewNotify)
24586     {
24587             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24588             return;
24589         }
24590         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24591             if(!keepExisting || this.singleSelect){
24592                 this.clearSelections();
24593             }
24594             
24595             var r = this.grid.store.getAt(index);
24596             //console.log('selectRow - record id :' + r.id);
24597             
24598             this.selections.add(r);
24599             this.last = this.lastActive = index;
24600             if(!preventViewNotify){
24601                 var proxy = new Roo.Element(
24602                                 this.grid.getRowDom(index)
24603                 );
24604                 proxy.addClass('bg-info info');
24605             }
24606             this.fireEvent("rowselect", this, index, r);
24607             this.fireEvent("selectionchange", this);
24608         }
24609     },
24610
24611     /**
24612      * Deselects a row.
24613      * @param {Number} row The index of the row to deselect
24614      */
24615     deselectRow : function(index, preventViewNotify)
24616     {
24617         if(this.locked) {
24618             return;
24619         }
24620         if(this.last == index){
24621             this.last = false;
24622         }
24623         if(this.lastActive == index){
24624             this.lastActive = false;
24625         }
24626         
24627         var r = this.grid.store.getAt(index);
24628         if (!r) {
24629             return;
24630         }
24631         
24632         this.selections.remove(r);
24633         //.console.log('deselectRow - record id :' + r.id);
24634         if(!preventViewNotify){
24635         
24636             var proxy = new Roo.Element(
24637                 this.grid.getRowDom(index)
24638             );
24639             proxy.removeClass('bg-info info');
24640         }
24641         this.fireEvent("rowdeselect", this, index);
24642         this.fireEvent("selectionchange", this);
24643     },
24644
24645     // private
24646     restoreLast : function(){
24647         if(this._last){
24648             this.last = this._last;
24649         }
24650     },
24651
24652     // private
24653     acceptsNav : function(row, col, cm){
24654         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24655     },
24656
24657     // private
24658     onEditorKey : function(field, e){
24659         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24660         if(k == e.TAB){
24661             e.stopEvent();
24662             ed.completeEdit();
24663             if(e.shiftKey){
24664                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24665             }else{
24666                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24667             }
24668         }else if(k == e.ENTER && !e.ctrlKey){
24669             e.stopEvent();
24670             ed.completeEdit();
24671             if(e.shiftKey){
24672                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24673             }else{
24674                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24675             }
24676         }else if(k == e.ESC){
24677             ed.cancelEdit();
24678         }
24679         if(newCell){
24680             g.startEditing(newCell[0], newCell[1]);
24681         }
24682     }
24683 });
24684 /*
24685  * Based on:
24686  * Ext JS Library 1.1.1
24687  * Copyright(c) 2006-2007, Ext JS, LLC.
24688  *
24689  * Originally Released Under LGPL - original licence link has changed is not relivant.
24690  *
24691  * Fork - LGPL
24692  * <script type="text/javascript">
24693  */
24694  
24695 /**
24696  * @class Roo.bootstrap.PagingToolbar
24697  * @extends Roo.bootstrap.NavSimplebar
24698  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24699  * @constructor
24700  * Create a new PagingToolbar
24701  * @param {Object} config The config object
24702  * @param {Roo.data.Store} store
24703  */
24704 Roo.bootstrap.PagingToolbar = function(config)
24705 {
24706     // old args format still supported... - xtype is prefered..
24707         // created from xtype...
24708     
24709     this.ds = config.dataSource;
24710     
24711     if (config.store && !this.ds) {
24712         this.store= Roo.factory(config.store, Roo.data);
24713         this.ds = this.store;
24714         this.ds.xmodule = this.xmodule || false;
24715     }
24716     
24717     this.toolbarItems = [];
24718     if (config.items) {
24719         this.toolbarItems = config.items;
24720     }
24721     
24722     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24723     
24724     this.cursor = 0;
24725     
24726     if (this.ds) { 
24727         this.bind(this.ds);
24728     }
24729     
24730     if (Roo.bootstrap.version == 4) {
24731         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24732     } else {
24733         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24734     }
24735     
24736 };
24737
24738 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24739     /**
24740      * @cfg {Roo.data.Store} dataSource
24741      * The underlying data store providing the paged data
24742      */
24743     /**
24744      * @cfg {String/HTMLElement/Element} container
24745      * container The id or element that will contain the toolbar
24746      */
24747     /**
24748      * @cfg {Boolean} displayInfo
24749      * True to display the displayMsg (defaults to false)
24750      */
24751     /**
24752      * @cfg {Number} pageSize
24753      * The number of records to display per page (defaults to 20)
24754      */
24755     pageSize: 20,
24756     /**
24757      * @cfg {String} displayMsg
24758      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24759      */
24760     displayMsg : 'Displaying {0} - {1} of {2}',
24761     /**
24762      * @cfg {String} emptyMsg
24763      * The message to display when no records are found (defaults to "No data to display")
24764      */
24765     emptyMsg : 'No data to display',
24766     /**
24767      * Customizable piece of the default paging text (defaults to "Page")
24768      * @type String
24769      */
24770     beforePageText : "Page",
24771     /**
24772      * Customizable piece of the default paging text (defaults to "of %0")
24773      * @type String
24774      */
24775     afterPageText : "of {0}",
24776     /**
24777      * Customizable piece of the default paging text (defaults to "First Page")
24778      * @type String
24779      */
24780     firstText : "First Page",
24781     /**
24782      * Customizable piece of the default paging text (defaults to "Previous Page")
24783      * @type String
24784      */
24785     prevText : "Previous Page",
24786     /**
24787      * Customizable piece of the default paging text (defaults to "Next Page")
24788      * @type String
24789      */
24790     nextText : "Next Page",
24791     /**
24792      * Customizable piece of the default paging text (defaults to "Last Page")
24793      * @type String
24794      */
24795     lastText : "Last Page",
24796     /**
24797      * Customizable piece of the default paging text (defaults to "Refresh")
24798      * @type String
24799      */
24800     refreshText : "Refresh",
24801
24802     buttons : false,
24803     // private
24804     onRender : function(ct, position) 
24805     {
24806         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24807         this.navgroup.parentId = this.id;
24808         this.navgroup.onRender(this.el, null);
24809         // add the buttons to the navgroup
24810         
24811         if(this.displayInfo){
24812             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24813             this.displayEl = this.el.select('.x-paging-info', true).first();
24814 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24815 //            this.displayEl = navel.el.select('span',true).first();
24816         }
24817         
24818         var _this = this;
24819         
24820         if(this.buttons){
24821             Roo.each(_this.buttons, function(e){ // this might need to use render????
24822                Roo.factory(e).render(_this.el);
24823             });
24824         }
24825             
24826         Roo.each(_this.toolbarItems, function(e) {
24827             _this.navgroup.addItem(e);
24828         });
24829         
24830         
24831         this.first = this.navgroup.addItem({
24832             tooltip: this.firstText,
24833             cls: "prev btn-outline-secondary",
24834             html : ' <i class="fa fa-step-backward"></i>',
24835             disabled: true,
24836             preventDefault: true,
24837             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24838         });
24839         
24840         this.prev =  this.navgroup.addItem({
24841             tooltip: this.prevText,
24842             cls: "prev btn-outline-secondary",
24843             html : ' <i class="fa fa-backward"></i>',
24844             disabled: true,
24845             preventDefault: true,
24846             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24847         });
24848     //this.addSeparator();
24849         
24850         
24851         var field = this.navgroup.addItem( {
24852             tagtype : 'span',
24853             cls : 'x-paging-position  btn-outline-secondary',
24854              disabled: true,
24855             html : this.beforePageText  +
24856                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24857                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24858          } ); //?? escaped?
24859         
24860         this.field = field.el.select('input', true).first();
24861         this.field.on("keydown", this.onPagingKeydown, this);
24862         this.field.on("focus", function(){this.dom.select();});
24863     
24864     
24865         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24866         //this.field.setHeight(18);
24867         //this.addSeparator();
24868         this.next = this.navgroup.addItem({
24869             tooltip: this.nextText,
24870             cls: "next btn-outline-secondary",
24871             html : ' <i class="fa fa-forward"></i>',
24872             disabled: true,
24873             preventDefault: true,
24874             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24875         });
24876         this.last = this.navgroup.addItem({
24877             tooltip: this.lastText,
24878             html : ' <i class="fa fa-step-forward"></i>',
24879             cls: "next btn-outline-secondary",
24880             disabled: true,
24881             preventDefault: true,
24882             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24883         });
24884     //this.addSeparator();
24885         this.loading = this.navgroup.addItem({
24886             tooltip: this.refreshText,
24887             cls: "btn-outline-secondary",
24888             html : ' <i class="fa fa-refresh"></i>',
24889             preventDefault: true,
24890             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24891         });
24892         
24893     },
24894
24895     // private
24896     updateInfo : function(){
24897         if(this.displayEl){
24898             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24899             var msg = count == 0 ?
24900                 this.emptyMsg :
24901                 String.format(
24902                     this.displayMsg,
24903                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24904                 );
24905             this.displayEl.update(msg);
24906         }
24907     },
24908
24909     // private
24910     onLoad : function(ds, r, o)
24911     {
24912         this.cursor = o.params.start ? o.params.start : 0;
24913         
24914         var d = this.getPageData(),
24915             ap = d.activePage,
24916             ps = d.pages;
24917         
24918         
24919         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24920         this.field.dom.value = ap;
24921         this.first.setDisabled(ap == 1);
24922         this.prev.setDisabled(ap == 1);
24923         this.next.setDisabled(ap == ps);
24924         this.last.setDisabled(ap == ps);
24925         this.loading.enable();
24926         this.updateInfo();
24927     },
24928
24929     // private
24930     getPageData : function(){
24931         var total = this.ds.getTotalCount();
24932         return {
24933             total : total,
24934             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24935             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24936         };
24937     },
24938
24939     // private
24940     onLoadError : function(){
24941         this.loading.enable();
24942     },
24943
24944     // private
24945     onPagingKeydown : function(e){
24946         var k = e.getKey();
24947         var d = this.getPageData();
24948         if(k == e.RETURN){
24949             var v = this.field.dom.value, pageNum;
24950             if(!v || isNaN(pageNum = parseInt(v, 10))){
24951                 this.field.dom.value = d.activePage;
24952                 return;
24953             }
24954             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24955             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24956             e.stopEvent();
24957         }
24958         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))
24959         {
24960           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24961           this.field.dom.value = pageNum;
24962           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24963           e.stopEvent();
24964         }
24965         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24966         {
24967           var v = this.field.dom.value, pageNum; 
24968           var increment = (e.shiftKey) ? 10 : 1;
24969           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24970                 increment *= -1;
24971           }
24972           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24973             this.field.dom.value = d.activePage;
24974             return;
24975           }
24976           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24977           {
24978             this.field.dom.value = parseInt(v, 10) + increment;
24979             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24980             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24981           }
24982           e.stopEvent();
24983         }
24984     },
24985
24986     // private
24987     beforeLoad : function(){
24988         if(this.loading){
24989             this.loading.disable();
24990         }
24991     },
24992
24993     // private
24994     onClick : function(which){
24995         
24996         var ds = this.ds;
24997         if (!ds) {
24998             return;
24999         }
25000         
25001         switch(which){
25002             case "first":
25003                 ds.load({params:{start: 0, limit: this.pageSize}});
25004             break;
25005             case "prev":
25006                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25007             break;
25008             case "next":
25009                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25010             break;
25011             case "last":
25012                 var total = ds.getTotalCount();
25013                 var extra = total % this.pageSize;
25014                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25015                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25016             break;
25017             case "refresh":
25018                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25019             break;
25020         }
25021     },
25022
25023     /**
25024      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25025      * @param {Roo.data.Store} store The data store to unbind
25026      */
25027     unbind : function(ds){
25028         ds.un("beforeload", this.beforeLoad, this);
25029         ds.un("load", this.onLoad, this);
25030         ds.un("loadexception", this.onLoadError, this);
25031         ds.un("remove", this.updateInfo, this);
25032         ds.un("add", this.updateInfo, this);
25033         this.ds = undefined;
25034     },
25035
25036     /**
25037      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25038      * @param {Roo.data.Store} store The data store to bind
25039      */
25040     bind : function(ds){
25041         ds.on("beforeload", this.beforeLoad, this);
25042         ds.on("load", this.onLoad, this);
25043         ds.on("loadexception", this.onLoadError, this);
25044         ds.on("remove", this.updateInfo, this);
25045         ds.on("add", this.updateInfo, this);
25046         this.ds = ds;
25047     }
25048 });/*
25049  * - LGPL
25050  *
25051  * element
25052  * 
25053  */
25054
25055 /**
25056  * @class Roo.bootstrap.MessageBar
25057  * @extends Roo.bootstrap.Component
25058  * Bootstrap MessageBar class
25059  * @cfg {String} html contents of the MessageBar
25060  * @cfg {String} weight (info | success | warning | danger) default info
25061  * @cfg {String} beforeClass insert the bar before the given class
25062  * @cfg {Boolean} closable (true | false) default false
25063  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25064  * 
25065  * @constructor
25066  * Create a new Element
25067  * @param {Object} config The config object
25068  */
25069
25070 Roo.bootstrap.MessageBar = function(config){
25071     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25072 };
25073
25074 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25075     
25076     html: '',
25077     weight: 'info',
25078     closable: false,
25079     fixed: false,
25080     beforeClass: 'bootstrap-sticky-wrap',
25081     
25082     getAutoCreate : function(){
25083         
25084         var cfg = {
25085             tag: 'div',
25086             cls: 'alert alert-dismissable alert-' + this.weight,
25087             cn: [
25088                 {
25089                     tag: 'span',
25090                     cls: 'message',
25091                     html: this.html || ''
25092                 }
25093             ]
25094         };
25095         
25096         if(this.fixed){
25097             cfg.cls += ' alert-messages-fixed';
25098         }
25099         
25100         if(this.closable){
25101             cfg.cn.push({
25102                 tag: 'button',
25103                 cls: 'close',
25104                 html: 'x'
25105             });
25106         }
25107         
25108         return cfg;
25109     },
25110     
25111     onRender : function(ct, position)
25112     {
25113         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25114         
25115         if(!this.el){
25116             var cfg = Roo.apply({},  this.getAutoCreate());
25117             cfg.id = Roo.id();
25118             
25119             if (this.cls) {
25120                 cfg.cls += ' ' + this.cls;
25121             }
25122             if (this.style) {
25123                 cfg.style = this.style;
25124             }
25125             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25126             
25127             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25128         }
25129         
25130         this.el.select('>button.close').on('click', this.hide, this);
25131         
25132     },
25133     
25134     show : function()
25135     {
25136         if (!this.rendered) {
25137             this.render();
25138         }
25139         
25140         this.el.show();
25141         
25142         this.fireEvent('show', this);
25143         
25144     },
25145     
25146     hide : function()
25147     {
25148         if (!this.rendered) {
25149             this.render();
25150         }
25151         
25152         this.el.hide();
25153         
25154         this.fireEvent('hide', this);
25155     },
25156     
25157     update : function()
25158     {
25159 //        var e = this.el.dom.firstChild;
25160 //        
25161 //        if(this.closable){
25162 //            e = e.nextSibling;
25163 //        }
25164 //        
25165 //        e.data = this.html || '';
25166
25167         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25168     }
25169    
25170 });
25171
25172  
25173
25174      /*
25175  * - LGPL
25176  *
25177  * Graph
25178  * 
25179  */
25180
25181
25182 /**
25183  * @class Roo.bootstrap.Graph
25184  * @extends Roo.bootstrap.Component
25185  * Bootstrap Graph class
25186 > Prameters
25187  -sm {number} sm 4
25188  -md {number} md 5
25189  @cfg {String} graphtype  bar | vbar | pie
25190  @cfg {number} g_x coodinator | centre x (pie)
25191  @cfg {number} g_y coodinator | centre y (pie)
25192  @cfg {number} g_r radius (pie)
25193  @cfg {number} g_height height of the chart (respected by all elements in the set)
25194  @cfg {number} g_width width of the chart (respected by all elements in the set)
25195  @cfg {Object} title The title of the chart
25196     
25197  -{Array}  values
25198  -opts (object) options for the chart 
25199      o {
25200      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25201      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25202      o vgutter (number)
25203      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.
25204      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25205      o to
25206      o stretch (boolean)
25207      o }
25208  -opts (object) options for the pie
25209      o{
25210      o cut
25211      o startAngle (number)
25212      o endAngle (number)
25213      } 
25214  *
25215  * @constructor
25216  * Create a new Input
25217  * @param {Object} config The config object
25218  */
25219
25220 Roo.bootstrap.Graph = function(config){
25221     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25222     
25223     this.addEvents({
25224         // img events
25225         /**
25226          * @event click
25227          * The img click event for the img.
25228          * @param {Roo.EventObject} e
25229          */
25230         "click" : true
25231     });
25232 };
25233
25234 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25235     
25236     sm: 4,
25237     md: 5,
25238     graphtype: 'bar',
25239     g_height: 250,
25240     g_width: 400,
25241     g_x: 50,
25242     g_y: 50,
25243     g_r: 30,
25244     opts:{
25245         //g_colors: this.colors,
25246         g_type: 'soft',
25247         g_gutter: '20%'
25248
25249     },
25250     title : false,
25251
25252     getAutoCreate : function(){
25253         
25254         var cfg = {
25255             tag: 'div',
25256             html : null
25257         };
25258         
25259         
25260         return  cfg;
25261     },
25262
25263     onRender : function(ct,position){
25264         
25265         
25266         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25267         
25268         if (typeof(Raphael) == 'undefined') {
25269             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25270             return;
25271         }
25272         
25273         this.raphael = Raphael(this.el.dom);
25274         
25275                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25276                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25277                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25278                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25279                 /*
25280                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25281                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25282                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25283                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25284                 
25285                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25286                 r.barchart(330, 10, 300, 220, data1);
25287                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25288                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25289                 */
25290                 
25291                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25292                 // r.barchart(30, 30, 560, 250,  xdata, {
25293                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25294                 //     axis : "0 0 1 1",
25295                 //     axisxlabels :  xdata
25296                 //     //yvalues : cols,
25297                    
25298                 // });
25299 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25300 //        
25301 //        this.load(null,xdata,{
25302 //                axis : "0 0 1 1",
25303 //                axisxlabels :  xdata
25304 //                });
25305
25306     },
25307
25308     load : function(graphtype,xdata,opts)
25309     {
25310         this.raphael.clear();
25311         if(!graphtype) {
25312             graphtype = this.graphtype;
25313         }
25314         if(!opts){
25315             opts = this.opts;
25316         }
25317         var r = this.raphael,
25318             fin = function () {
25319                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25320             },
25321             fout = function () {
25322                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25323             },
25324             pfin = function() {
25325                 this.sector.stop();
25326                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25327
25328                 if (this.label) {
25329                     this.label[0].stop();
25330                     this.label[0].attr({ r: 7.5 });
25331                     this.label[1].attr({ "font-weight": 800 });
25332                 }
25333             },
25334             pfout = function() {
25335                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25336
25337                 if (this.label) {
25338                     this.label[0].animate({ r: 5 }, 500, "bounce");
25339                     this.label[1].attr({ "font-weight": 400 });
25340                 }
25341             };
25342
25343         switch(graphtype){
25344             case 'bar':
25345                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25346                 break;
25347             case 'hbar':
25348                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25349                 break;
25350             case 'pie':
25351 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25352 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25353 //            
25354                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25355                 
25356                 break;
25357
25358         }
25359         
25360         if(this.title){
25361             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25362         }
25363         
25364     },
25365     
25366     setTitle: function(o)
25367     {
25368         this.title = o;
25369     },
25370     
25371     initEvents: function() {
25372         
25373         if(!this.href){
25374             this.el.on('click', this.onClick, this);
25375         }
25376     },
25377     
25378     onClick : function(e)
25379     {
25380         Roo.log('img onclick');
25381         this.fireEvent('click', this, e);
25382     }
25383    
25384 });
25385
25386  
25387 /*
25388  * - LGPL
25389  *
25390  * numberBox
25391  * 
25392  */
25393 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25394
25395 /**
25396  * @class Roo.bootstrap.dash.NumberBox
25397  * @extends Roo.bootstrap.Component
25398  * Bootstrap NumberBox class
25399  * @cfg {String} headline Box headline
25400  * @cfg {String} content Box content
25401  * @cfg {String} icon Box icon
25402  * @cfg {String} footer Footer text
25403  * @cfg {String} fhref Footer href
25404  * 
25405  * @constructor
25406  * Create a new NumberBox
25407  * @param {Object} config The config object
25408  */
25409
25410
25411 Roo.bootstrap.dash.NumberBox = function(config){
25412     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25413     
25414 };
25415
25416 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25417     
25418     headline : '',
25419     content : '',
25420     icon : '',
25421     footer : '',
25422     fhref : '',
25423     ficon : '',
25424     
25425     getAutoCreate : function(){
25426         
25427         var cfg = {
25428             tag : 'div',
25429             cls : 'small-box ',
25430             cn : [
25431                 {
25432                     tag : 'div',
25433                     cls : 'inner',
25434                     cn :[
25435                         {
25436                             tag : 'h3',
25437                             cls : 'roo-headline',
25438                             html : this.headline
25439                         },
25440                         {
25441                             tag : 'p',
25442                             cls : 'roo-content',
25443                             html : this.content
25444                         }
25445                     ]
25446                 }
25447             ]
25448         };
25449         
25450         if(this.icon){
25451             cfg.cn.push({
25452                 tag : 'div',
25453                 cls : 'icon',
25454                 cn :[
25455                     {
25456                         tag : 'i',
25457                         cls : 'ion ' + this.icon
25458                     }
25459                 ]
25460             });
25461         }
25462         
25463         if(this.footer){
25464             var footer = {
25465                 tag : 'a',
25466                 cls : 'small-box-footer',
25467                 href : this.fhref || '#',
25468                 html : this.footer
25469             };
25470             
25471             cfg.cn.push(footer);
25472             
25473         }
25474         
25475         return  cfg;
25476     },
25477
25478     onRender : function(ct,position){
25479         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25480
25481
25482        
25483                 
25484     },
25485
25486     setHeadline: function (value)
25487     {
25488         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25489     },
25490     
25491     setFooter: function (value, href)
25492     {
25493         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25494         
25495         if(href){
25496             this.el.select('a.small-box-footer',true).first().attr('href', href);
25497         }
25498         
25499     },
25500
25501     setContent: function (value)
25502     {
25503         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25504     },
25505
25506     initEvents: function() 
25507     {   
25508         
25509     }
25510     
25511 });
25512
25513  
25514 /*
25515  * - LGPL
25516  *
25517  * TabBox
25518  * 
25519  */
25520 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25521
25522 /**
25523  * @class Roo.bootstrap.dash.TabBox
25524  * @extends Roo.bootstrap.Component
25525  * Bootstrap TabBox class
25526  * @cfg {String} title Title of the TabBox
25527  * @cfg {String} icon Icon of the TabBox
25528  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25529  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25530  * 
25531  * @constructor
25532  * Create a new TabBox
25533  * @param {Object} config The config object
25534  */
25535
25536
25537 Roo.bootstrap.dash.TabBox = function(config){
25538     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25539     this.addEvents({
25540         // raw events
25541         /**
25542          * @event addpane
25543          * When a pane is added
25544          * @param {Roo.bootstrap.dash.TabPane} pane
25545          */
25546         "addpane" : true,
25547         /**
25548          * @event activatepane
25549          * When a pane is activated
25550          * @param {Roo.bootstrap.dash.TabPane} pane
25551          */
25552         "activatepane" : true
25553         
25554          
25555     });
25556     
25557     this.panes = [];
25558 };
25559
25560 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25561
25562     title : '',
25563     icon : false,
25564     showtabs : true,
25565     tabScrollable : false,
25566     
25567     getChildContainer : function()
25568     {
25569         return this.el.select('.tab-content', true).first();
25570     },
25571     
25572     getAutoCreate : function(){
25573         
25574         var header = {
25575             tag: 'li',
25576             cls: 'pull-left header',
25577             html: this.title,
25578             cn : []
25579         };
25580         
25581         if(this.icon){
25582             header.cn.push({
25583                 tag: 'i',
25584                 cls: 'fa ' + this.icon
25585             });
25586         }
25587         
25588         var h = {
25589             tag: 'ul',
25590             cls: 'nav nav-tabs pull-right',
25591             cn: [
25592                 header
25593             ]
25594         };
25595         
25596         if(this.tabScrollable){
25597             h = {
25598                 tag: 'div',
25599                 cls: 'tab-header',
25600                 cn: [
25601                     {
25602                         tag: 'ul',
25603                         cls: 'nav nav-tabs pull-right',
25604                         cn: [
25605                             header
25606                         ]
25607                     }
25608                 ]
25609             };
25610         }
25611         
25612         var cfg = {
25613             tag: 'div',
25614             cls: 'nav-tabs-custom',
25615             cn: [
25616                 h,
25617                 {
25618                     tag: 'div',
25619                     cls: 'tab-content no-padding',
25620                     cn: []
25621                 }
25622             ]
25623         };
25624
25625         return  cfg;
25626     },
25627     initEvents : function()
25628     {
25629         //Roo.log('add add pane handler');
25630         this.on('addpane', this.onAddPane, this);
25631     },
25632      /**
25633      * Updates the box title
25634      * @param {String} html to set the title to.
25635      */
25636     setTitle : function(value)
25637     {
25638         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25639     },
25640     onAddPane : function(pane)
25641     {
25642         this.panes.push(pane);
25643         //Roo.log('addpane');
25644         //Roo.log(pane);
25645         // tabs are rendere left to right..
25646         if(!this.showtabs){
25647             return;
25648         }
25649         
25650         var ctr = this.el.select('.nav-tabs', true).first();
25651          
25652          
25653         var existing = ctr.select('.nav-tab',true);
25654         var qty = existing.getCount();;
25655         
25656         
25657         var tab = ctr.createChild({
25658             tag : 'li',
25659             cls : 'nav-tab' + (qty ? '' : ' active'),
25660             cn : [
25661                 {
25662                     tag : 'a',
25663                     href:'#',
25664                     html : pane.title
25665                 }
25666             ]
25667         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25668         pane.tab = tab;
25669         
25670         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25671         if (!qty) {
25672             pane.el.addClass('active');
25673         }
25674         
25675                 
25676     },
25677     onTabClick : function(ev,un,ob,pane)
25678     {
25679         //Roo.log('tab - prev default');
25680         ev.preventDefault();
25681         
25682         
25683         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25684         pane.tab.addClass('active');
25685         //Roo.log(pane.title);
25686         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25687         // technically we should have a deactivate event.. but maybe add later.
25688         // and it should not de-activate the selected tab...
25689         this.fireEvent('activatepane', pane);
25690         pane.el.addClass('active');
25691         pane.fireEvent('activate');
25692         
25693         
25694     },
25695     
25696     getActivePane : function()
25697     {
25698         var r = false;
25699         Roo.each(this.panes, function(p) {
25700             if(p.el.hasClass('active')){
25701                 r = p;
25702                 return false;
25703             }
25704             
25705             return;
25706         });
25707         
25708         return r;
25709     }
25710     
25711     
25712 });
25713
25714  
25715 /*
25716  * - LGPL
25717  *
25718  * Tab pane
25719  * 
25720  */
25721 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25722 /**
25723  * @class Roo.bootstrap.TabPane
25724  * @extends Roo.bootstrap.Component
25725  * Bootstrap TabPane class
25726  * @cfg {Boolean} active (false | true) Default false
25727  * @cfg {String} title title of panel
25728
25729  * 
25730  * @constructor
25731  * Create a new TabPane
25732  * @param {Object} config The config object
25733  */
25734
25735 Roo.bootstrap.dash.TabPane = function(config){
25736     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25737     
25738     this.addEvents({
25739         // raw events
25740         /**
25741          * @event activate
25742          * When a pane is activated
25743          * @param {Roo.bootstrap.dash.TabPane} pane
25744          */
25745         "activate" : true
25746          
25747     });
25748 };
25749
25750 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25751     
25752     active : false,
25753     title : '',
25754     
25755     // the tabBox that this is attached to.
25756     tab : false,
25757      
25758     getAutoCreate : function() 
25759     {
25760         var cfg = {
25761             tag: 'div',
25762             cls: 'tab-pane'
25763         };
25764         
25765         if(this.active){
25766             cfg.cls += ' active';
25767         }
25768         
25769         return cfg;
25770     },
25771     initEvents  : function()
25772     {
25773         //Roo.log('trigger add pane handler');
25774         this.parent().fireEvent('addpane', this)
25775     },
25776     
25777      /**
25778      * Updates the tab title 
25779      * @param {String} html to set the title to.
25780      */
25781     setTitle: function(str)
25782     {
25783         if (!this.tab) {
25784             return;
25785         }
25786         this.title = str;
25787         this.tab.select('a', true).first().dom.innerHTML = str;
25788         
25789     }
25790     
25791     
25792     
25793 });
25794
25795  
25796
25797
25798  /*
25799  * - LGPL
25800  *
25801  * menu
25802  * 
25803  */
25804 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25805
25806 /**
25807  * @class Roo.bootstrap.menu.Menu
25808  * @extends Roo.bootstrap.Component
25809  * Bootstrap Menu class - container for Menu
25810  * @cfg {String} html Text of the menu
25811  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25812  * @cfg {String} icon Font awesome icon
25813  * @cfg {String} pos Menu align to (top | bottom) default bottom
25814  * 
25815  * 
25816  * @constructor
25817  * Create a new Menu
25818  * @param {Object} config The config object
25819  */
25820
25821
25822 Roo.bootstrap.menu.Menu = function(config){
25823     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25824     
25825     this.addEvents({
25826         /**
25827          * @event beforeshow
25828          * Fires before this menu is displayed
25829          * @param {Roo.bootstrap.menu.Menu} this
25830          */
25831         beforeshow : true,
25832         /**
25833          * @event beforehide
25834          * Fires before this menu is hidden
25835          * @param {Roo.bootstrap.menu.Menu} this
25836          */
25837         beforehide : true,
25838         /**
25839          * @event show
25840          * Fires after this menu is displayed
25841          * @param {Roo.bootstrap.menu.Menu} this
25842          */
25843         show : true,
25844         /**
25845          * @event hide
25846          * Fires after this menu is hidden
25847          * @param {Roo.bootstrap.menu.Menu} this
25848          */
25849         hide : true,
25850         /**
25851          * @event click
25852          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25853          * @param {Roo.bootstrap.menu.Menu} this
25854          * @param {Roo.EventObject} e
25855          */
25856         click : true
25857     });
25858     
25859 };
25860
25861 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25862     
25863     submenu : false,
25864     html : '',
25865     weight : 'default',
25866     icon : false,
25867     pos : 'bottom',
25868     
25869     
25870     getChildContainer : function() {
25871         if(this.isSubMenu){
25872             return this.el;
25873         }
25874         
25875         return this.el.select('ul.dropdown-menu', true).first();  
25876     },
25877     
25878     getAutoCreate : function()
25879     {
25880         var text = [
25881             {
25882                 tag : 'span',
25883                 cls : 'roo-menu-text',
25884                 html : this.html
25885             }
25886         ];
25887         
25888         if(this.icon){
25889             text.unshift({
25890                 tag : 'i',
25891                 cls : 'fa ' + this.icon
25892             })
25893         }
25894         
25895         
25896         var cfg = {
25897             tag : 'div',
25898             cls : 'btn-group',
25899             cn : [
25900                 {
25901                     tag : 'button',
25902                     cls : 'dropdown-button btn btn-' + this.weight,
25903                     cn : text
25904                 },
25905                 {
25906                     tag : 'button',
25907                     cls : 'dropdown-toggle btn btn-' + this.weight,
25908                     cn : [
25909                         {
25910                             tag : 'span',
25911                             cls : 'caret'
25912                         }
25913                     ]
25914                 },
25915                 {
25916                     tag : 'ul',
25917                     cls : 'dropdown-menu'
25918                 }
25919             ]
25920             
25921         };
25922         
25923         if(this.pos == 'top'){
25924             cfg.cls += ' dropup';
25925         }
25926         
25927         if(this.isSubMenu){
25928             cfg = {
25929                 tag : 'ul',
25930                 cls : 'dropdown-menu'
25931             }
25932         }
25933         
25934         return cfg;
25935     },
25936     
25937     onRender : function(ct, position)
25938     {
25939         this.isSubMenu = ct.hasClass('dropdown-submenu');
25940         
25941         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25942     },
25943     
25944     initEvents : function() 
25945     {
25946         if(this.isSubMenu){
25947             return;
25948         }
25949         
25950         this.hidden = true;
25951         
25952         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25953         this.triggerEl.on('click', this.onTriggerPress, this);
25954         
25955         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25956         this.buttonEl.on('click', this.onClick, this);
25957         
25958     },
25959     
25960     list : function()
25961     {
25962         if(this.isSubMenu){
25963             return this.el;
25964         }
25965         
25966         return this.el.select('ul.dropdown-menu', true).first();
25967     },
25968     
25969     onClick : function(e)
25970     {
25971         this.fireEvent("click", this, e);
25972     },
25973     
25974     onTriggerPress  : function(e)
25975     {   
25976         if (this.isVisible()) {
25977             this.hide();
25978         } else {
25979             this.show();
25980         }
25981     },
25982     
25983     isVisible : function(){
25984         return !this.hidden;
25985     },
25986     
25987     show : function()
25988     {
25989         this.fireEvent("beforeshow", this);
25990         
25991         this.hidden = false;
25992         this.el.addClass('open');
25993         
25994         Roo.get(document).on("mouseup", this.onMouseUp, this);
25995         
25996         this.fireEvent("show", this);
25997         
25998         
25999     },
26000     
26001     hide : function()
26002     {
26003         this.fireEvent("beforehide", this);
26004         
26005         this.hidden = true;
26006         this.el.removeClass('open');
26007         
26008         Roo.get(document).un("mouseup", this.onMouseUp);
26009         
26010         this.fireEvent("hide", this);
26011     },
26012     
26013     onMouseUp : function()
26014     {
26015         this.hide();
26016     }
26017     
26018 });
26019
26020  
26021  /*
26022  * - LGPL
26023  *
26024  * menu item
26025  * 
26026  */
26027 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26028
26029 /**
26030  * @class Roo.bootstrap.menu.Item
26031  * @extends Roo.bootstrap.Component
26032  * Bootstrap MenuItem class
26033  * @cfg {Boolean} submenu (true | false) default false
26034  * @cfg {String} html text of the item
26035  * @cfg {String} href the link
26036  * @cfg {Boolean} disable (true | false) default false
26037  * @cfg {Boolean} preventDefault (true | false) default true
26038  * @cfg {String} icon Font awesome icon
26039  * @cfg {String} pos Submenu align to (left | right) default right 
26040  * 
26041  * 
26042  * @constructor
26043  * Create a new Item
26044  * @param {Object} config The config object
26045  */
26046
26047
26048 Roo.bootstrap.menu.Item = function(config){
26049     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26050     this.addEvents({
26051         /**
26052          * @event mouseover
26053          * Fires when the mouse is hovering over this menu
26054          * @param {Roo.bootstrap.menu.Item} this
26055          * @param {Roo.EventObject} e
26056          */
26057         mouseover : true,
26058         /**
26059          * @event mouseout
26060          * Fires when the mouse exits this menu
26061          * @param {Roo.bootstrap.menu.Item} this
26062          * @param {Roo.EventObject} e
26063          */
26064         mouseout : true,
26065         // raw events
26066         /**
26067          * @event click
26068          * The raw click event for the entire grid.
26069          * @param {Roo.EventObject} e
26070          */
26071         click : true
26072     });
26073 };
26074
26075 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26076     
26077     submenu : false,
26078     href : '',
26079     html : '',
26080     preventDefault: true,
26081     disable : false,
26082     icon : false,
26083     pos : 'right',
26084     
26085     getAutoCreate : function()
26086     {
26087         var text = [
26088             {
26089                 tag : 'span',
26090                 cls : 'roo-menu-item-text',
26091                 html : this.html
26092             }
26093         ];
26094         
26095         if(this.icon){
26096             text.unshift({
26097                 tag : 'i',
26098                 cls : 'fa ' + this.icon
26099             })
26100         }
26101         
26102         var cfg = {
26103             tag : 'li',
26104             cn : [
26105                 {
26106                     tag : 'a',
26107                     href : this.href || '#',
26108                     cn : text
26109                 }
26110             ]
26111         };
26112         
26113         if(this.disable){
26114             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26115         }
26116         
26117         if(this.submenu){
26118             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26119             
26120             if(this.pos == 'left'){
26121                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26122             }
26123         }
26124         
26125         return cfg;
26126     },
26127     
26128     initEvents : function() 
26129     {
26130         this.el.on('mouseover', this.onMouseOver, this);
26131         this.el.on('mouseout', this.onMouseOut, this);
26132         
26133         this.el.select('a', true).first().on('click', this.onClick, this);
26134         
26135     },
26136     
26137     onClick : function(e)
26138     {
26139         if(this.preventDefault){
26140             e.preventDefault();
26141         }
26142         
26143         this.fireEvent("click", this, e);
26144     },
26145     
26146     onMouseOver : function(e)
26147     {
26148         if(this.submenu && this.pos == 'left'){
26149             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26150         }
26151         
26152         this.fireEvent("mouseover", this, e);
26153     },
26154     
26155     onMouseOut : function(e)
26156     {
26157         this.fireEvent("mouseout", this, e);
26158     }
26159 });
26160
26161  
26162
26163  /*
26164  * - LGPL
26165  *
26166  * menu separator
26167  * 
26168  */
26169 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26170
26171 /**
26172  * @class Roo.bootstrap.menu.Separator
26173  * @extends Roo.bootstrap.Component
26174  * Bootstrap Separator class
26175  * 
26176  * @constructor
26177  * Create a new Separator
26178  * @param {Object} config The config object
26179  */
26180
26181
26182 Roo.bootstrap.menu.Separator = function(config){
26183     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26184 };
26185
26186 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26187     
26188     getAutoCreate : function(){
26189         var cfg = {
26190             tag : 'li',
26191             cls: 'divider'
26192         };
26193         
26194         return cfg;
26195     }
26196    
26197 });
26198
26199  
26200
26201  /*
26202  * - LGPL
26203  *
26204  * Tooltip
26205  * 
26206  */
26207
26208 /**
26209  * @class Roo.bootstrap.Tooltip
26210  * Bootstrap Tooltip class
26211  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26212  * to determine which dom element triggers the tooltip.
26213  * 
26214  * It needs to add support for additional attributes like tooltip-position
26215  * 
26216  * @constructor
26217  * Create a new Toolti
26218  * @param {Object} config The config object
26219  */
26220
26221 Roo.bootstrap.Tooltip = function(config){
26222     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26223     
26224     this.alignment = Roo.bootstrap.Tooltip.alignment;
26225     
26226     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26227         this.alignment = config.alignment;
26228     }
26229     
26230 };
26231
26232 Roo.apply(Roo.bootstrap.Tooltip, {
26233     /**
26234      * @function init initialize tooltip monitoring.
26235      * @static
26236      */
26237     currentEl : false,
26238     currentTip : false,
26239     currentRegion : false,
26240     
26241     //  init : delay?
26242     
26243     init : function()
26244     {
26245         Roo.get(document).on('mouseover', this.enter ,this);
26246         Roo.get(document).on('mouseout', this.leave, this);
26247          
26248         
26249         this.currentTip = new Roo.bootstrap.Tooltip();
26250     },
26251     
26252     enter : function(ev)
26253     {
26254         var dom = ev.getTarget();
26255         
26256         //Roo.log(['enter',dom]);
26257         var el = Roo.fly(dom);
26258         if (this.currentEl) {
26259             //Roo.log(dom);
26260             //Roo.log(this.currentEl);
26261             //Roo.log(this.currentEl.contains(dom));
26262             if (this.currentEl == el) {
26263                 return;
26264             }
26265             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26266                 return;
26267             }
26268
26269         }
26270         
26271         if (this.currentTip.el) {
26272             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26273         }    
26274         //Roo.log(ev);
26275         
26276         if(!el || el.dom == document){
26277             return;
26278         }
26279         
26280         var bindEl = el;
26281         
26282         // you can not look for children, as if el is the body.. then everythign is the child..
26283         if (!el.attr('tooltip')) { //
26284             if (!el.select("[tooltip]").elements.length) {
26285                 return;
26286             }
26287             // is the mouse over this child...?
26288             bindEl = el.select("[tooltip]").first();
26289             var xy = ev.getXY();
26290             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26291                 //Roo.log("not in region.");
26292                 return;
26293             }
26294             //Roo.log("child element over..");
26295             
26296         }
26297         this.currentEl = bindEl;
26298         this.currentTip.bind(bindEl);
26299         this.currentRegion = Roo.lib.Region.getRegion(dom);
26300         this.currentTip.enter();
26301         
26302     },
26303     leave : function(ev)
26304     {
26305         var dom = ev.getTarget();
26306         //Roo.log(['leave',dom]);
26307         if (!this.currentEl) {
26308             return;
26309         }
26310         
26311         
26312         if (dom != this.currentEl.dom) {
26313             return;
26314         }
26315         var xy = ev.getXY();
26316         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26317             return;
26318         }
26319         // only activate leave if mouse cursor is outside... bounding box..
26320         
26321         
26322         
26323         
26324         if (this.currentTip) {
26325             this.currentTip.leave();
26326         }
26327         //Roo.log('clear currentEl');
26328         this.currentEl = false;
26329         
26330         
26331     },
26332     alignment : {
26333         'left' : ['r-l', [-2,0], 'right'],
26334         'right' : ['l-r', [2,0], 'left'],
26335         'bottom' : ['t-b', [0,2], 'top'],
26336         'top' : [ 'b-t', [0,-2], 'bottom']
26337     }
26338     
26339 });
26340
26341
26342 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26343     
26344     
26345     bindEl : false,
26346     
26347     delay : null, // can be { show : 300 , hide: 500}
26348     
26349     timeout : null,
26350     
26351     hoverState : null, //???
26352     
26353     placement : 'bottom', 
26354     
26355     alignment : false,
26356     
26357     getAutoCreate : function(){
26358     
26359         var cfg = {
26360            cls : 'tooltip',
26361            role : 'tooltip',
26362            cn : [
26363                 {
26364                     cls : 'tooltip-arrow'
26365                 },
26366                 {
26367                     cls : 'tooltip-inner'
26368                 }
26369            ]
26370         };
26371         
26372         return cfg;
26373     },
26374     bind : function(el)
26375     {
26376         this.bindEl = el;
26377     },
26378       
26379     
26380     enter : function () {
26381        
26382         if (this.timeout != null) {
26383             clearTimeout(this.timeout);
26384         }
26385         
26386         this.hoverState = 'in';
26387          //Roo.log("enter - show");
26388         if (!this.delay || !this.delay.show) {
26389             this.show();
26390             return;
26391         }
26392         var _t = this;
26393         this.timeout = setTimeout(function () {
26394             if (_t.hoverState == 'in') {
26395                 _t.show();
26396             }
26397         }, this.delay.show);
26398     },
26399     leave : function()
26400     {
26401         clearTimeout(this.timeout);
26402     
26403         this.hoverState = 'out';
26404          if (!this.delay || !this.delay.hide) {
26405             this.hide();
26406             return;
26407         }
26408        
26409         var _t = this;
26410         this.timeout = setTimeout(function () {
26411             //Roo.log("leave - timeout");
26412             
26413             if (_t.hoverState == 'out') {
26414                 _t.hide();
26415                 Roo.bootstrap.Tooltip.currentEl = false;
26416             }
26417         }, delay);
26418     },
26419     
26420     show : function (msg)
26421     {
26422         if (!this.el) {
26423             this.render(document.body);
26424         }
26425         // set content.
26426         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26427         
26428         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26429         
26430         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26431         
26432         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26433         
26434         var placement = typeof this.placement == 'function' ?
26435             this.placement.call(this, this.el, on_el) :
26436             this.placement;
26437             
26438         var autoToken = /\s?auto?\s?/i;
26439         var autoPlace = autoToken.test(placement);
26440         if (autoPlace) {
26441             placement = placement.replace(autoToken, '') || 'top';
26442         }
26443         
26444         //this.el.detach()
26445         //this.el.setXY([0,0]);
26446         this.el.show();
26447         //this.el.dom.style.display='block';
26448         
26449         //this.el.appendTo(on_el);
26450         
26451         var p = this.getPosition();
26452         var box = this.el.getBox();
26453         
26454         if (autoPlace) {
26455             // fixme..
26456         }
26457         
26458         var align = this.alignment[placement];
26459         
26460         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26461         
26462         if(placement == 'top' || placement == 'bottom'){
26463             if(xy[0] < 0){
26464                 placement = 'right';
26465             }
26466             
26467             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26468                 placement = 'left';
26469             }
26470             
26471             var scroll = Roo.select('body', true).first().getScroll();
26472             
26473             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26474                 placement = 'top';
26475             }
26476             
26477             align = this.alignment[placement];
26478         }
26479         
26480         this.el.alignTo(this.bindEl, align[0],align[1]);
26481         //var arrow = this.el.select('.arrow',true).first();
26482         //arrow.set(align[2], 
26483         
26484         this.el.addClass(placement);
26485         
26486         this.el.addClass('in fade');
26487         
26488         this.hoverState = null;
26489         
26490         if (this.el.hasClass('fade')) {
26491             // fade it?
26492         }
26493         
26494     },
26495     hide : function()
26496     {
26497          
26498         if (!this.el) {
26499             return;
26500         }
26501         //this.el.setXY([0,0]);
26502         this.el.removeClass('in');
26503         //this.el.hide();
26504         
26505     }
26506     
26507 });
26508  
26509
26510  /*
26511  * - LGPL
26512  *
26513  * Location Picker
26514  * 
26515  */
26516
26517 /**
26518  * @class Roo.bootstrap.LocationPicker
26519  * @extends Roo.bootstrap.Component
26520  * Bootstrap LocationPicker class
26521  * @cfg {Number} latitude Position when init default 0
26522  * @cfg {Number} longitude Position when init default 0
26523  * @cfg {Number} zoom default 15
26524  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26525  * @cfg {Boolean} mapTypeControl default false
26526  * @cfg {Boolean} disableDoubleClickZoom default false
26527  * @cfg {Boolean} scrollwheel default true
26528  * @cfg {Boolean} streetViewControl default false
26529  * @cfg {Number} radius default 0
26530  * @cfg {String} locationName
26531  * @cfg {Boolean} draggable default true
26532  * @cfg {Boolean} enableAutocomplete default false
26533  * @cfg {Boolean} enableReverseGeocode default true
26534  * @cfg {String} markerTitle
26535  * 
26536  * @constructor
26537  * Create a new LocationPicker
26538  * @param {Object} config The config object
26539  */
26540
26541
26542 Roo.bootstrap.LocationPicker = function(config){
26543     
26544     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26545     
26546     this.addEvents({
26547         /**
26548          * @event initial
26549          * Fires when the picker initialized.
26550          * @param {Roo.bootstrap.LocationPicker} this
26551          * @param {Google Location} location
26552          */
26553         initial : true,
26554         /**
26555          * @event positionchanged
26556          * Fires when the picker position changed.
26557          * @param {Roo.bootstrap.LocationPicker} this
26558          * @param {Google Location} location
26559          */
26560         positionchanged : true,
26561         /**
26562          * @event resize
26563          * Fires when the map resize.
26564          * @param {Roo.bootstrap.LocationPicker} this
26565          */
26566         resize : true,
26567         /**
26568          * @event show
26569          * Fires when the map show.
26570          * @param {Roo.bootstrap.LocationPicker} this
26571          */
26572         show : true,
26573         /**
26574          * @event hide
26575          * Fires when the map hide.
26576          * @param {Roo.bootstrap.LocationPicker} this
26577          */
26578         hide : true,
26579         /**
26580          * @event mapClick
26581          * Fires when click the map.
26582          * @param {Roo.bootstrap.LocationPicker} this
26583          * @param {Map event} e
26584          */
26585         mapClick : true,
26586         /**
26587          * @event mapRightClick
26588          * Fires when right click the map.
26589          * @param {Roo.bootstrap.LocationPicker} this
26590          * @param {Map event} e
26591          */
26592         mapRightClick : true,
26593         /**
26594          * @event markerClick
26595          * Fires when click the marker.
26596          * @param {Roo.bootstrap.LocationPicker} this
26597          * @param {Map event} e
26598          */
26599         markerClick : true,
26600         /**
26601          * @event markerRightClick
26602          * Fires when right click the marker.
26603          * @param {Roo.bootstrap.LocationPicker} this
26604          * @param {Map event} e
26605          */
26606         markerRightClick : true,
26607         /**
26608          * @event OverlayViewDraw
26609          * Fires when OverlayView Draw
26610          * @param {Roo.bootstrap.LocationPicker} this
26611          */
26612         OverlayViewDraw : true,
26613         /**
26614          * @event OverlayViewOnAdd
26615          * Fires when OverlayView Draw
26616          * @param {Roo.bootstrap.LocationPicker} this
26617          */
26618         OverlayViewOnAdd : true,
26619         /**
26620          * @event OverlayViewOnRemove
26621          * Fires when OverlayView Draw
26622          * @param {Roo.bootstrap.LocationPicker} this
26623          */
26624         OverlayViewOnRemove : true,
26625         /**
26626          * @event OverlayViewShow
26627          * Fires when OverlayView Draw
26628          * @param {Roo.bootstrap.LocationPicker} this
26629          * @param {Pixel} cpx
26630          */
26631         OverlayViewShow : true,
26632         /**
26633          * @event OverlayViewHide
26634          * Fires when OverlayView Draw
26635          * @param {Roo.bootstrap.LocationPicker} this
26636          */
26637         OverlayViewHide : true,
26638         /**
26639          * @event loadexception
26640          * Fires when load google lib failed.
26641          * @param {Roo.bootstrap.LocationPicker} this
26642          */
26643         loadexception : true
26644     });
26645         
26646 };
26647
26648 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26649     
26650     gMapContext: false,
26651     
26652     latitude: 0,
26653     longitude: 0,
26654     zoom: 15,
26655     mapTypeId: false,
26656     mapTypeControl: false,
26657     disableDoubleClickZoom: false,
26658     scrollwheel: true,
26659     streetViewControl: false,
26660     radius: 0,
26661     locationName: '',
26662     draggable: true,
26663     enableAutocomplete: false,
26664     enableReverseGeocode: true,
26665     markerTitle: '',
26666     
26667     getAutoCreate: function()
26668     {
26669
26670         var cfg = {
26671             tag: 'div',
26672             cls: 'roo-location-picker'
26673         };
26674         
26675         return cfg
26676     },
26677     
26678     initEvents: function(ct, position)
26679     {       
26680         if(!this.el.getWidth() || this.isApplied()){
26681             return;
26682         }
26683         
26684         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26685         
26686         this.initial();
26687     },
26688     
26689     initial: function()
26690     {
26691         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26692             this.fireEvent('loadexception', this);
26693             return;
26694         }
26695         
26696         if(!this.mapTypeId){
26697             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26698         }
26699         
26700         this.gMapContext = this.GMapContext();
26701         
26702         this.initOverlayView();
26703         
26704         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26705         
26706         var _this = this;
26707                 
26708         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26709             _this.setPosition(_this.gMapContext.marker.position);
26710         });
26711         
26712         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26713             _this.fireEvent('mapClick', this, event);
26714             
26715         });
26716
26717         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26718             _this.fireEvent('mapRightClick', this, event);
26719             
26720         });
26721         
26722         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26723             _this.fireEvent('markerClick', this, event);
26724             
26725         });
26726
26727         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26728             _this.fireEvent('markerRightClick', this, event);
26729             
26730         });
26731         
26732         this.setPosition(this.gMapContext.location);
26733         
26734         this.fireEvent('initial', this, this.gMapContext.location);
26735     },
26736     
26737     initOverlayView: function()
26738     {
26739         var _this = this;
26740         
26741         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26742             
26743             draw: function()
26744             {
26745                 _this.fireEvent('OverlayViewDraw', _this);
26746             },
26747             
26748             onAdd: function()
26749             {
26750                 _this.fireEvent('OverlayViewOnAdd', _this);
26751             },
26752             
26753             onRemove: function()
26754             {
26755                 _this.fireEvent('OverlayViewOnRemove', _this);
26756             },
26757             
26758             show: function(cpx)
26759             {
26760                 _this.fireEvent('OverlayViewShow', _this, cpx);
26761             },
26762             
26763             hide: function()
26764             {
26765                 _this.fireEvent('OverlayViewHide', _this);
26766             }
26767             
26768         });
26769     },
26770     
26771     fromLatLngToContainerPixel: function(event)
26772     {
26773         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26774     },
26775     
26776     isApplied: function() 
26777     {
26778         return this.getGmapContext() == false ? false : true;
26779     },
26780     
26781     getGmapContext: function() 
26782     {
26783         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26784     },
26785     
26786     GMapContext: function() 
26787     {
26788         var position = new google.maps.LatLng(this.latitude, this.longitude);
26789         
26790         var _map = new google.maps.Map(this.el.dom, {
26791             center: position,
26792             zoom: this.zoom,
26793             mapTypeId: this.mapTypeId,
26794             mapTypeControl: this.mapTypeControl,
26795             disableDoubleClickZoom: this.disableDoubleClickZoom,
26796             scrollwheel: this.scrollwheel,
26797             streetViewControl: this.streetViewControl,
26798             locationName: this.locationName,
26799             draggable: this.draggable,
26800             enableAutocomplete: this.enableAutocomplete,
26801             enableReverseGeocode: this.enableReverseGeocode
26802         });
26803         
26804         var _marker = new google.maps.Marker({
26805             position: position,
26806             map: _map,
26807             title: this.markerTitle,
26808             draggable: this.draggable
26809         });
26810         
26811         return {
26812             map: _map,
26813             marker: _marker,
26814             circle: null,
26815             location: position,
26816             radius: this.radius,
26817             locationName: this.locationName,
26818             addressComponents: {
26819                 formatted_address: null,
26820                 addressLine1: null,
26821                 addressLine2: null,
26822                 streetName: null,
26823                 streetNumber: null,
26824                 city: null,
26825                 district: null,
26826                 state: null,
26827                 stateOrProvince: null
26828             },
26829             settings: this,
26830             domContainer: this.el.dom,
26831             geodecoder: new google.maps.Geocoder()
26832         };
26833     },
26834     
26835     drawCircle: function(center, radius, options) 
26836     {
26837         if (this.gMapContext.circle != null) {
26838             this.gMapContext.circle.setMap(null);
26839         }
26840         if (radius > 0) {
26841             radius *= 1;
26842             options = Roo.apply({}, options, {
26843                 strokeColor: "#0000FF",
26844                 strokeOpacity: .35,
26845                 strokeWeight: 2,
26846                 fillColor: "#0000FF",
26847                 fillOpacity: .2
26848             });
26849             
26850             options.map = this.gMapContext.map;
26851             options.radius = radius;
26852             options.center = center;
26853             this.gMapContext.circle = new google.maps.Circle(options);
26854             return this.gMapContext.circle;
26855         }
26856         
26857         return null;
26858     },
26859     
26860     setPosition: function(location) 
26861     {
26862         this.gMapContext.location = location;
26863         this.gMapContext.marker.setPosition(location);
26864         this.gMapContext.map.panTo(location);
26865         this.drawCircle(location, this.gMapContext.radius, {});
26866         
26867         var _this = this;
26868         
26869         if (this.gMapContext.settings.enableReverseGeocode) {
26870             this.gMapContext.geodecoder.geocode({
26871                 latLng: this.gMapContext.location
26872             }, function(results, status) {
26873                 
26874                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26875                     _this.gMapContext.locationName = results[0].formatted_address;
26876                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26877                     
26878                     _this.fireEvent('positionchanged', this, location);
26879                 }
26880             });
26881             
26882             return;
26883         }
26884         
26885         this.fireEvent('positionchanged', this, location);
26886     },
26887     
26888     resize: function()
26889     {
26890         google.maps.event.trigger(this.gMapContext.map, "resize");
26891         
26892         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26893         
26894         this.fireEvent('resize', this);
26895     },
26896     
26897     setPositionByLatLng: function(latitude, longitude)
26898     {
26899         this.setPosition(new google.maps.LatLng(latitude, longitude));
26900     },
26901     
26902     getCurrentPosition: function() 
26903     {
26904         return {
26905             latitude: this.gMapContext.location.lat(),
26906             longitude: this.gMapContext.location.lng()
26907         };
26908     },
26909     
26910     getAddressName: function() 
26911     {
26912         return this.gMapContext.locationName;
26913     },
26914     
26915     getAddressComponents: function() 
26916     {
26917         return this.gMapContext.addressComponents;
26918     },
26919     
26920     address_component_from_google_geocode: function(address_components) 
26921     {
26922         var result = {};
26923         
26924         for (var i = 0; i < address_components.length; i++) {
26925             var component = address_components[i];
26926             if (component.types.indexOf("postal_code") >= 0) {
26927                 result.postalCode = component.short_name;
26928             } else if (component.types.indexOf("street_number") >= 0) {
26929                 result.streetNumber = component.short_name;
26930             } else if (component.types.indexOf("route") >= 0) {
26931                 result.streetName = component.short_name;
26932             } else if (component.types.indexOf("neighborhood") >= 0) {
26933                 result.city = component.short_name;
26934             } else if (component.types.indexOf("locality") >= 0) {
26935                 result.city = component.short_name;
26936             } else if (component.types.indexOf("sublocality") >= 0) {
26937                 result.district = component.short_name;
26938             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26939                 result.stateOrProvince = component.short_name;
26940             } else if (component.types.indexOf("country") >= 0) {
26941                 result.country = component.short_name;
26942             }
26943         }
26944         
26945         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26946         result.addressLine2 = "";
26947         return result;
26948     },
26949     
26950     setZoomLevel: function(zoom)
26951     {
26952         this.gMapContext.map.setZoom(zoom);
26953     },
26954     
26955     show: function()
26956     {
26957         if(!this.el){
26958             return;
26959         }
26960         
26961         this.el.show();
26962         
26963         this.resize();
26964         
26965         this.fireEvent('show', this);
26966     },
26967     
26968     hide: function()
26969     {
26970         if(!this.el){
26971             return;
26972         }
26973         
26974         this.el.hide();
26975         
26976         this.fireEvent('hide', this);
26977     }
26978     
26979 });
26980
26981 Roo.apply(Roo.bootstrap.LocationPicker, {
26982     
26983     OverlayView : function(map, options)
26984     {
26985         options = options || {};
26986         
26987         this.setMap(map);
26988     }
26989     
26990     
26991 });/*
26992  * - LGPL
26993  *
26994  * Alert
26995  * 
26996  */
26997
26998 /**
26999  * @class Roo.bootstrap.Alert
27000  * @extends Roo.bootstrap.Component
27001  * Bootstrap Alert class
27002  * @cfg {String} title The title of alert
27003  * @cfg {String} html The content of alert
27004  * @cfg {String} weight (  success | info | warning | danger )
27005  * @cfg {String} faicon font-awesomeicon
27006  * 
27007  * @constructor
27008  * Create a new alert
27009  * @param {Object} config The config object
27010  */
27011
27012
27013 Roo.bootstrap.Alert = function(config){
27014     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27015     
27016 };
27017
27018 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27019     
27020     title: '',
27021     html: '',
27022     weight: false,
27023     faicon: false,
27024     
27025     getAutoCreate : function()
27026     {
27027         
27028         var cfg = {
27029             tag : 'div',
27030             cls : 'alert',
27031             cn : [
27032                 {
27033                     tag : 'i',
27034                     cls : 'roo-alert-icon'
27035                     
27036                 },
27037                 {
27038                     tag : 'b',
27039                     cls : 'roo-alert-title',
27040                     html : this.title
27041                 },
27042                 {
27043                     tag : 'span',
27044                     cls : 'roo-alert-text',
27045                     html : this.html
27046                 }
27047             ]
27048         };
27049         
27050         if(this.faicon){
27051             cfg.cn[0].cls += ' fa ' + this.faicon;
27052         }
27053         
27054         if(this.weight){
27055             cfg.cls += ' alert-' + this.weight;
27056         }
27057         
27058         return cfg;
27059     },
27060     
27061     initEvents: function() 
27062     {
27063         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27064     },
27065     
27066     setTitle : function(str)
27067     {
27068         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27069     },
27070     
27071     setText : function(str)
27072     {
27073         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27074     },
27075     
27076     setWeight : function(weight)
27077     {
27078         if(this.weight){
27079             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27080         }
27081         
27082         this.weight = weight;
27083         
27084         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27085     },
27086     
27087     setIcon : function(icon)
27088     {
27089         if(this.faicon){
27090             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27091         }
27092         
27093         this.faicon = icon;
27094         
27095         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27096     },
27097     
27098     hide: function() 
27099     {
27100         this.el.hide();   
27101     },
27102     
27103     show: function() 
27104     {  
27105         this.el.show();   
27106     }
27107     
27108 });
27109
27110  
27111 /*
27112 * Licence: LGPL
27113 */
27114
27115 /**
27116  * @class Roo.bootstrap.UploadCropbox
27117  * @extends Roo.bootstrap.Component
27118  * Bootstrap UploadCropbox class
27119  * @cfg {String} emptyText show when image has been loaded
27120  * @cfg {String} rotateNotify show when image too small to rotate
27121  * @cfg {Number} errorTimeout default 3000
27122  * @cfg {Number} minWidth default 300
27123  * @cfg {Number} minHeight default 300
27124  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27125  * @cfg {Boolean} isDocument (true|false) default false
27126  * @cfg {String} url action url
27127  * @cfg {String} paramName default 'imageUpload'
27128  * @cfg {String} method default POST
27129  * @cfg {Boolean} loadMask (true|false) default true
27130  * @cfg {Boolean} loadingText default 'Loading...'
27131  * 
27132  * @constructor
27133  * Create a new UploadCropbox
27134  * @param {Object} config The config object
27135  */
27136
27137 Roo.bootstrap.UploadCropbox = function(config){
27138     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27139     
27140     this.addEvents({
27141         /**
27142          * @event beforeselectfile
27143          * Fire before select file
27144          * @param {Roo.bootstrap.UploadCropbox} this
27145          */
27146         "beforeselectfile" : true,
27147         /**
27148          * @event initial
27149          * Fire after initEvent
27150          * @param {Roo.bootstrap.UploadCropbox} this
27151          */
27152         "initial" : true,
27153         /**
27154          * @event crop
27155          * Fire after initEvent
27156          * @param {Roo.bootstrap.UploadCropbox} this
27157          * @param {String} data
27158          */
27159         "crop" : true,
27160         /**
27161          * @event prepare
27162          * Fire when preparing the file data
27163          * @param {Roo.bootstrap.UploadCropbox} this
27164          * @param {Object} file
27165          */
27166         "prepare" : true,
27167         /**
27168          * @event exception
27169          * Fire when get exception
27170          * @param {Roo.bootstrap.UploadCropbox} this
27171          * @param {XMLHttpRequest} xhr
27172          */
27173         "exception" : true,
27174         /**
27175          * @event beforeloadcanvas
27176          * Fire before load the canvas
27177          * @param {Roo.bootstrap.UploadCropbox} this
27178          * @param {String} src
27179          */
27180         "beforeloadcanvas" : true,
27181         /**
27182          * @event trash
27183          * Fire when trash image
27184          * @param {Roo.bootstrap.UploadCropbox} this
27185          */
27186         "trash" : true,
27187         /**
27188          * @event download
27189          * Fire when download the image
27190          * @param {Roo.bootstrap.UploadCropbox} this
27191          */
27192         "download" : true,
27193         /**
27194          * @event footerbuttonclick
27195          * Fire when footerbuttonclick
27196          * @param {Roo.bootstrap.UploadCropbox} this
27197          * @param {String} type
27198          */
27199         "footerbuttonclick" : true,
27200         /**
27201          * @event resize
27202          * Fire when resize
27203          * @param {Roo.bootstrap.UploadCropbox} this
27204          */
27205         "resize" : true,
27206         /**
27207          * @event rotate
27208          * Fire when rotate the image
27209          * @param {Roo.bootstrap.UploadCropbox} this
27210          * @param {String} pos
27211          */
27212         "rotate" : true,
27213         /**
27214          * @event inspect
27215          * Fire when inspect the file
27216          * @param {Roo.bootstrap.UploadCropbox} this
27217          * @param {Object} file
27218          */
27219         "inspect" : true,
27220         /**
27221          * @event upload
27222          * Fire when xhr upload the file
27223          * @param {Roo.bootstrap.UploadCropbox} this
27224          * @param {Object} data
27225          */
27226         "upload" : true,
27227         /**
27228          * @event arrange
27229          * Fire when arrange the file data
27230          * @param {Roo.bootstrap.UploadCropbox} this
27231          * @param {Object} formData
27232          */
27233         "arrange" : true
27234     });
27235     
27236     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27237 };
27238
27239 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27240     
27241     emptyText : 'Click to upload image',
27242     rotateNotify : 'Image is too small to rotate',
27243     errorTimeout : 3000,
27244     scale : 0,
27245     baseScale : 1,
27246     rotate : 0,
27247     dragable : false,
27248     pinching : false,
27249     mouseX : 0,
27250     mouseY : 0,
27251     cropData : false,
27252     minWidth : 300,
27253     minHeight : 300,
27254     file : false,
27255     exif : {},
27256     baseRotate : 1,
27257     cropType : 'image/jpeg',
27258     buttons : false,
27259     canvasLoaded : false,
27260     isDocument : false,
27261     method : 'POST',
27262     paramName : 'imageUpload',
27263     loadMask : true,
27264     loadingText : 'Loading...',
27265     maskEl : false,
27266     
27267     getAutoCreate : function()
27268     {
27269         var cfg = {
27270             tag : 'div',
27271             cls : 'roo-upload-cropbox',
27272             cn : [
27273                 {
27274                     tag : 'input',
27275                     cls : 'roo-upload-cropbox-selector',
27276                     type : 'file'
27277                 },
27278                 {
27279                     tag : 'div',
27280                     cls : 'roo-upload-cropbox-body',
27281                     style : 'cursor:pointer',
27282                     cn : [
27283                         {
27284                             tag : 'div',
27285                             cls : 'roo-upload-cropbox-preview'
27286                         },
27287                         {
27288                             tag : 'div',
27289                             cls : 'roo-upload-cropbox-thumb'
27290                         },
27291                         {
27292                             tag : 'div',
27293                             cls : 'roo-upload-cropbox-empty-notify',
27294                             html : this.emptyText
27295                         },
27296                         {
27297                             tag : 'div',
27298                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27299                             html : this.rotateNotify
27300                         }
27301                     ]
27302                 },
27303                 {
27304                     tag : 'div',
27305                     cls : 'roo-upload-cropbox-footer',
27306                     cn : {
27307                         tag : 'div',
27308                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27309                         cn : []
27310                     }
27311                 }
27312             ]
27313         };
27314         
27315         return cfg;
27316     },
27317     
27318     onRender : function(ct, position)
27319     {
27320         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27321         
27322         if (this.buttons.length) {
27323             
27324             Roo.each(this.buttons, function(bb) {
27325                 
27326                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27327                 
27328                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27329                 
27330             }, this);
27331         }
27332         
27333         if(this.loadMask){
27334             this.maskEl = this.el;
27335         }
27336     },
27337     
27338     initEvents : function()
27339     {
27340         this.urlAPI = (window.createObjectURL && window) || 
27341                                 (window.URL && URL.revokeObjectURL && URL) || 
27342                                 (window.webkitURL && webkitURL);
27343                         
27344         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27345         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27346         
27347         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27348         this.selectorEl.hide();
27349         
27350         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27351         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27352         
27353         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27354         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27355         this.thumbEl.hide();
27356         
27357         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27358         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27359         
27360         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27361         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27362         this.errorEl.hide();
27363         
27364         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27365         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27366         this.footerEl.hide();
27367         
27368         this.setThumbBoxSize();
27369         
27370         this.bind();
27371         
27372         this.resize();
27373         
27374         this.fireEvent('initial', this);
27375     },
27376
27377     bind : function()
27378     {
27379         var _this = this;
27380         
27381         window.addEventListener("resize", function() { _this.resize(); } );
27382         
27383         this.bodyEl.on('click', this.beforeSelectFile, this);
27384         
27385         if(Roo.isTouch){
27386             this.bodyEl.on('touchstart', this.onTouchStart, this);
27387             this.bodyEl.on('touchmove', this.onTouchMove, this);
27388             this.bodyEl.on('touchend', this.onTouchEnd, this);
27389         }
27390         
27391         if(!Roo.isTouch){
27392             this.bodyEl.on('mousedown', this.onMouseDown, this);
27393             this.bodyEl.on('mousemove', this.onMouseMove, this);
27394             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27395             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27396             Roo.get(document).on('mouseup', this.onMouseUp, this);
27397         }
27398         
27399         this.selectorEl.on('change', this.onFileSelected, this);
27400     },
27401     
27402     reset : function()
27403     {    
27404         this.scale = 0;
27405         this.baseScale = 1;
27406         this.rotate = 0;
27407         this.baseRotate = 1;
27408         this.dragable = false;
27409         this.pinching = false;
27410         this.mouseX = 0;
27411         this.mouseY = 0;
27412         this.cropData = false;
27413         this.notifyEl.dom.innerHTML = this.emptyText;
27414         
27415         this.selectorEl.dom.value = '';
27416         
27417     },
27418     
27419     resize : function()
27420     {
27421         if(this.fireEvent('resize', this) != false){
27422             this.setThumbBoxPosition();
27423             this.setCanvasPosition();
27424         }
27425     },
27426     
27427     onFooterButtonClick : function(e, el, o, type)
27428     {
27429         switch (type) {
27430             case 'rotate-left' :
27431                 this.onRotateLeft(e);
27432                 break;
27433             case 'rotate-right' :
27434                 this.onRotateRight(e);
27435                 break;
27436             case 'picture' :
27437                 this.beforeSelectFile(e);
27438                 break;
27439             case 'trash' :
27440                 this.trash(e);
27441                 break;
27442             case 'crop' :
27443                 this.crop(e);
27444                 break;
27445             case 'download' :
27446                 this.download(e);
27447                 break;
27448             default :
27449                 break;
27450         }
27451         
27452         this.fireEvent('footerbuttonclick', this, type);
27453     },
27454     
27455     beforeSelectFile : function(e)
27456     {
27457         e.preventDefault();
27458         
27459         if(this.fireEvent('beforeselectfile', this) != false){
27460             this.selectorEl.dom.click();
27461         }
27462     },
27463     
27464     onFileSelected : function(e)
27465     {
27466         e.preventDefault();
27467         
27468         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27469             return;
27470         }
27471         
27472         var file = this.selectorEl.dom.files[0];
27473         
27474         if(this.fireEvent('inspect', this, file) != false){
27475             this.prepare(file);
27476         }
27477         
27478     },
27479     
27480     trash : function(e)
27481     {
27482         this.fireEvent('trash', this);
27483     },
27484     
27485     download : function(e)
27486     {
27487         this.fireEvent('download', this);
27488     },
27489     
27490     loadCanvas : function(src)
27491     {   
27492         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27493             
27494             this.reset();
27495             
27496             this.imageEl = document.createElement('img');
27497             
27498             var _this = this;
27499             
27500             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27501             
27502             this.imageEl.src = src;
27503         }
27504     },
27505     
27506     onLoadCanvas : function()
27507     {   
27508         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27509         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27510         
27511         this.bodyEl.un('click', this.beforeSelectFile, this);
27512         
27513         this.notifyEl.hide();
27514         this.thumbEl.show();
27515         this.footerEl.show();
27516         
27517         this.baseRotateLevel();
27518         
27519         if(this.isDocument){
27520             this.setThumbBoxSize();
27521         }
27522         
27523         this.setThumbBoxPosition();
27524         
27525         this.baseScaleLevel();
27526         
27527         this.draw();
27528         
27529         this.resize();
27530         
27531         this.canvasLoaded = true;
27532         
27533         if(this.loadMask){
27534             this.maskEl.unmask();
27535         }
27536         
27537     },
27538     
27539     setCanvasPosition : function()
27540     {   
27541         if(!this.canvasEl){
27542             return;
27543         }
27544         
27545         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27546         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27547         
27548         this.previewEl.setLeft(pw);
27549         this.previewEl.setTop(ph);
27550         
27551     },
27552     
27553     onMouseDown : function(e)
27554     {   
27555         e.stopEvent();
27556         
27557         this.dragable = true;
27558         this.pinching = false;
27559         
27560         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27561             this.dragable = false;
27562             return;
27563         }
27564         
27565         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27566         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27567         
27568     },
27569     
27570     onMouseMove : function(e)
27571     {   
27572         e.stopEvent();
27573         
27574         if(!this.canvasLoaded){
27575             return;
27576         }
27577         
27578         if (!this.dragable){
27579             return;
27580         }
27581         
27582         var minX = Math.ceil(this.thumbEl.getLeft(true));
27583         var minY = Math.ceil(this.thumbEl.getTop(true));
27584         
27585         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27586         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27587         
27588         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27589         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27590         
27591         x = x - this.mouseX;
27592         y = y - this.mouseY;
27593         
27594         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27595         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27596         
27597         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27598         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27599         
27600         this.previewEl.setLeft(bgX);
27601         this.previewEl.setTop(bgY);
27602         
27603         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27604         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27605     },
27606     
27607     onMouseUp : function(e)
27608     {   
27609         e.stopEvent();
27610         
27611         this.dragable = false;
27612     },
27613     
27614     onMouseWheel : function(e)
27615     {   
27616         e.stopEvent();
27617         
27618         this.startScale = this.scale;
27619         
27620         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27621         
27622         if(!this.zoomable()){
27623             this.scale = this.startScale;
27624             return;
27625         }
27626         
27627         this.draw();
27628         
27629         return;
27630     },
27631     
27632     zoomable : function()
27633     {
27634         var minScale = this.thumbEl.getWidth() / this.minWidth;
27635         
27636         if(this.minWidth < this.minHeight){
27637             minScale = this.thumbEl.getHeight() / this.minHeight;
27638         }
27639         
27640         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27641         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27642         
27643         if(
27644                 this.isDocument &&
27645                 (this.rotate == 0 || this.rotate == 180) && 
27646                 (
27647                     width > this.imageEl.OriginWidth || 
27648                     height > this.imageEl.OriginHeight ||
27649                     (width < this.minWidth && height < this.minHeight)
27650                 )
27651         ){
27652             return false;
27653         }
27654         
27655         if(
27656                 this.isDocument &&
27657                 (this.rotate == 90 || this.rotate == 270) && 
27658                 (
27659                     width > this.imageEl.OriginWidth || 
27660                     height > this.imageEl.OriginHeight ||
27661                     (width < this.minHeight && height < this.minWidth)
27662                 )
27663         ){
27664             return false;
27665         }
27666         
27667         if(
27668                 !this.isDocument &&
27669                 (this.rotate == 0 || this.rotate == 180) && 
27670                 (
27671                     width < this.minWidth || 
27672                     width > this.imageEl.OriginWidth || 
27673                     height < this.minHeight || 
27674                     height > this.imageEl.OriginHeight
27675                 )
27676         ){
27677             return false;
27678         }
27679         
27680         if(
27681                 !this.isDocument &&
27682                 (this.rotate == 90 || this.rotate == 270) && 
27683                 (
27684                     width < this.minHeight || 
27685                     width > this.imageEl.OriginWidth || 
27686                     height < this.minWidth || 
27687                     height > this.imageEl.OriginHeight
27688                 )
27689         ){
27690             return false;
27691         }
27692         
27693         return true;
27694         
27695     },
27696     
27697     onRotateLeft : function(e)
27698     {   
27699         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27700             
27701             var minScale = this.thumbEl.getWidth() / this.minWidth;
27702             
27703             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27704             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27705             
27706             this.startScale = this.scale;
27707             
27708             while (this.getScaleLevel() < minScale){
27709             
27710                 this.scale = this.scale + 1;
27711                 
27712                 if(!this.zoomable()){
27713                     break;
27714                 }
27715                 
27716                 if(
27717                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27718                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27719                 ){
27720                     continue;
27721                 }
27722                 
27723                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27724
27725                 this.draw();
27726                 
27727                 return;
27728             }
27729             
27730             this.scale = this.startScale;
27731             
27732             this.onRotateFail();
27733             
27734             return false;
27735         }
27736         
27737         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27738
27739         if(this.isDocument){
27740             this.setThumbBoxSize();
27741             this.setThumbBoxPosition();
27742             this.setCanvasPosition();
27743         }
27744         
27745         this.draw();
27746         
27747         this.fireEvent('rotate', this, 'left');
27748         
27749     },
27750     
27751     onRotateRight : function(e)
27752     {
27753         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27754             
27755             var minScale = this.thumbEl.getWidth() / this.minWidth;
27756         
27757             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27758             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27759             
27760             this.startScale = this.scale;
27761             
27762             while (this.getScaleLevel() < minScale){
27763             
27764                 this.scale = this.scale + 1;
27765                 
27766                 if(!this.zoomable()){
27767                     break;
27768                 }
27769                 
27770                 if(
27771                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27772                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27773                 ){
27774                     continue;
27775                 }
27776                 
27777                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27778
27779                 this.draw();
27780                 
27781                 return;
27782             }
27783             
27784             this.scale = this.startScale;
27785             
27786             this.onRotateFail();
27787             
27788             return false;
27789         }
27790         
27791         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27792
27793         if(this.isDocument){
27794             this.setThumbBoxSize();
27795             this.setThumbBoxPosition();
27796             this.setCanvasPosition();
27797         }
27798         
27799         this.draw();
27800         
27801         this.fireEvent('rotate', this, 'right');
27802     },
27803     
27804     onRotateFail : function()
27805     {
27806         this.errorEl.show(true);
27807         
27808         var _this = this;
27809         
27810         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27811     },
27812     
27813     draw : function()
27814     {
27815         this.previewEl.dom.innerHTML = '';
27816         
27817         var canvasEl = document.createElement("canvas");
27818         
27819         var contextEl = canvasEl.getContext("2d");
27820         
27821         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27822         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27823         var center = this.imageEl.OriginWidth / 2;
27824         
27825         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27826             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27827             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27828             center = this.imageEl.OriginHeight / 2;
27829         }
27830         
27831         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27832         
27833         contextEl.translate(center, center);
27834         contextEl.rotate(this.rotate * Math.PI / 180);
27835
27836         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27837         
27838         this.canvasEl = document.createElement("canvas");
27839         
27840         this.contextEl = this.canvasEl.getContext("2d");
27841         
27842         switch (this.rotate) {
27843             case 0 :
27844                 
27845                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27846                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27847                 
27848                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27849                 
27850                 break;
27851             case 90 : 
27852                 
27853                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27854                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27855                 
27856                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27857                     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);
27858                     break;
27859                 }
27860                 
27861                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27862                 
27863                 break;
27864             case 180 :
27865                 
27866                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27867                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27868                 
27869                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27870                     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);
27871                     break;
27872                 }
27873                 
27874                 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);
27875                 
27876                 break;
27877             case 270 :
27878                 
27879                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27880                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27881         
27882                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27883                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27884                     break;
27885                 }
27886                 
27887                 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);
27888                 
27889                 break;
27890             default : 
27891                 break;
27892         }
27893         
27894         this.previewEl.appendChild(this.canvasEl);
27895         
27896         this.setCanvasPosition();
27897     },
27898     
27899     crop : function()
27900     {
27901         if(!this.canvasLoaded){
27902             return;
27903         }
27904         
27905         var imageCanvas = document.createElement("canvas");
27906         
27907         var imageContext = imageCanvas.getContext("2d");
27908         
27909         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27910         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27911         
27912         var center = imageCanvas.width / 2;
27913         
27914         imageContext.translate(center, center);
27915         
27916         imageContext.rotate(this.rotate * Math.PI / 180);
27917         
27918         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27919         
27920         var canvas = document.createElement("canvas");
27921         
27922         var context = canvas.getContext("2d");
27923                 
27924         canvas.width = this.minWidth;
27925         canvas.height = this.minHeight;
27926
27927         switch (this.rotate) {
27928             case 0 :
27929                 
27930                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27931                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27932                 
27933                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27934                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27935                 
27936                 var targetWidth = this.minWidth - 2 * x;
27937                 var targetHeight = this.minHeight - 2 * y;
27938                 
27939                 var scale = 1;
27940                 
27941                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27942                     scale = targetWidth / width;
27943                 }
27944                 
27945                 if(x > 0 && y == 0){
27946                     scale = targetHeight / height;
27947                 }
27948                 
27949                 if(x > 0 && y > 0){
27950                     scale = targetWidth / width;
27951                     
27952                     if(width < height){
27953                         scale = targetHeight / height;
27954                     }
27955                 }
27956                 
27957                 context.scale(scale, scale);
27958                 
27959                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27960                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27961
27962                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27963                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27964
27965                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27966                 
27967                 break;
27968             case 90 : 
27969                 
27970                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27971                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27972                 
27973                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27974                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27975                 
27976                 var targetWidth = this.minWidth - 2 * x;
27977                 var targetHeight = this.minHeight - 2 * y;
27978                 
27979                 var scale = 1;
27980                 
27981                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27982                     scale = targetWidth / width;
27983                 }
27984                 
27985                 if(x > 0 && y == 0){
27986                     scale = targetHeight / height;
27987                 }
27988                 
27989                 if(x > 0 && y > 0){
27990                     scale = targetWidth / width;
27991                     
27992                     if(width < height){
27993                         scale = targetHeight / height;
27994                     }
27995                 }
27996                 
27997                 context.scale(scale, scale);
27998                 
27999                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28000                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28001
28002                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28003                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28004                 
28005                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28006                 
28007                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28008                 
28009                 break;
28010             case 180 :
28011                 
28012                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28013                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28014                 
28015                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28016                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28017                 
28018                 var targetWidth = this.minWidth - 2 * x;
28019                 var targetHeight = this.minHeight - 2 * y;
28020                 
28021                 var scale = 1;
28022                 
28023                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28024                     scale = targetWidth / width;
28025                 }
28026                 
28027                 if(x > 0 && y == 0){
28028                     scale = targetHeight / height;
28029                 }
28030                 
28031                 if(x > 0 && y > 0){
28032                     scale = targetWidth / width;
28033                     
28034                     if(width < height){
28035                         scale = targetHeight / height;
28036                     }
28037                 }
28038                 
28039                 context.scale(scale, scale);
28040                 
28041                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28042                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28043
28044                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28045                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28046
28047                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28048                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28049                 
28050                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28051                 
28052                 break;
28053             case 270 :
28054                 
28055                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28056                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28057                 
28058                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28059                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28060                 
28061                 var targetWidth = this.minWidth - 2 * x;
28062                 var targetHeight = this.minHeight - 2 * y;
28063                 
28064                 var scale = 1;
28065                 
28066                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28067                     scale = targetWidth / width;
28068                 }
28069                 
28070                 if(x > 0 && y == 0){
28071                     scale = targetHeight / height;
28072                 }
28073                 
28074                 if(x > 0 && y > 0){
28075                     scale = targetWidth / width;
28076                     
28077                     if(width < height){
28078                         scale = targetHeight / height;
28079                     }
28080                 }
28081                 
28082                 context.scale(scale, scale);
28083                 
28084                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28085                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28086
28087                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28088                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28089                 
28090                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28091                 
28092                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28093                 
28094                 break;
28095             default : 
28096                 break;
28097         }
28098         
28099         this.cropData = canvas.toDataURL(this.cropType);
28100         
28101         if(this.fireEvent('crop', this, this.cropData) !== false){
28102             this.process(this.file, this.cropData);
28103         }
28104         
28105         return;
28106         
28107     },
28108     
28109     setThumbBoxSize : function()
28110     {
28111         var width, height;
28112         
28113         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28114             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28115             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28116             
28117             this.minWidth = width;
28118             this.minHeight = height;
28119             
28120             if(this.rotate == 90 || this.rotate == 270){
28121                 this.minWidth = height;
28122                 this.minHeight = width;
28123             }
28124         }
28125         
28126         height = 300;
28127         width = Math.ceil(this.minWidth * height / this.minHeight);
28128         
28129         if(this.minWidth > this.minHeight){
28130             width = 300;
28131             height = Math.ceil(this.minHeight * width / this.minWidth);
28132         }
28133         
28134         this.thumbEl.setStyle({
28135             width : width + 'px',
28136             height : height + 'px'
28137         });
28138
28139         return;
28140             
28141     },
28142     
28143     setThumbBoxPosition : function()
28144     {
28145         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28146         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28147         
28148         this.thumbEl.setLeft(x);
28149         this.thumbEl.setTop(y);
28150         
28151     },
28152     
28153     baseRotateLevel : function()
28154     {
28155         this.baseRotate = 1;
28156         
28157         if(
28158                 typeof(this.exif) != 'undefined' &&
28159                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28160                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28161         ){
28162             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28163         }
28164         
28165         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28166         
28167     },
28168     
28169     baseScaleLevel : function()
28170     {
28171         var width, height;
28172         
28173         if(this.isDocument){
28174             
28175             if(this.baseRotate == 6 || this.baseRotate == 8){
28176             
28177                 height = this.thumbEl.getHeight();
28178                 this.baseScale = height / this.imageEl.OriginWidth;
28179
28180                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28181                     width = this.thumbEl.getWidth();
28182                     this.baseScale = width / this.imageEl.OriginHeight;
28183                 }
28184
28185                 return;
28186             }
28187
28188             height = this.thumbEl.getHeight();
28189             this.baseScale = height / this.imageEl.OriginHeight;
28190
28191             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28192                 width = this.thumbEl.getWidth();
28193                 this.baseScale = width / this.imageEl.OriginWidth;
28194             }
28195
28196             return;
28197         }
28198         
28199         if(this.baseRotate == 6 || this.baseRotate == 8){
28200             
28201             width = this.thumbEl.getHeight();
28202             this.baseScale = width / this.imageEl.OriginHeight;
28203             
28204             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28205                 height = this.thumbEl.getWidth();
28206                 this.baseScale = height / this.imageEl.OriginHeight;
28207             }
28208             
28209             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28210                 height = this.thumbEl.getWidth();
28211                 this.baseScale = height / this.imageEl.OriginHeight;
28212                 
28213                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28214                     width = this.thumbEl.getHeight();
28215                     this.baseScale = width / this.imageEl.OriginWidth;
28216                 }
28217             }
28218             
28219             return;
28220         }
28221         
28222         width = this.thumbEl.getWidth();
28223         this.baseScale = width / this.imageEl.OriginWidth;
28224         
28225         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28226             height = this.thumbEl.getHeight();
28227             this.baseScale = height / this.imageEl.OriginHeight;
28228         }
28229         
28230         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28231             
28232             height = this.thumbEl.getHeight();
28233             this.baseScale = height / this.imageEl.OriginHeight;
28234             
28235             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28236                 width = this.thumbEl.getWidth();
28237                 this.baseScale = width / this.imageEl.OriginWidth;
28238             }
28239             
28240         }
28241         
28242         return;
28243     },
28244     
28245     getScaleLevel : function()
28246     {
28247         return this.baseScale * Math.pow(1.1, this.scale);
28248     },
28249     
28250     onTouchStart : function(e)
28251     {
28252         if(!this.canvasLoaded){
28253             this.beforeSelectFile(e);
28254             return;
28255         }
28256         
28257         var touches = e.browserEvent.touches;
28258         
28259         if(!touches){
28260             return;
28261         }
28262         
28263         if(touches.length == 1){
28264             this.onMouseDown(e);
28265             return;
28266         }
28267         
28268         if(touches.length != 2){
28269             return;
28270         }
28271         
28272         var coords = [];
28273         
28274         for(var i = 0, finger; finger = touches[i]; i++){
28275             coords.push(finger.pageX, finger.pageY);
28276         }
28277         
28278         var x = Math.pow(coords[0] - coords[2], 2);
28279         var y = Math.pow(coords[1] - coords[3], 2);
28280         
28281         this.startDistance = Math.sqrt(x + y);
28282         
28283         this.startScale = this.scale;
28284         
28285         this.pinching = true;
28286         this.dragable = false;
28287         
28288     },
28289     
28290     onTouchMove : function(e)
28291     {
28292         if(!this.pinching && !this.dragable){
28293             return;
28294         }
28295         
28296         var touches = e.browserEvent.touches;
28297         
28298         if(!touches){
28299             return;
28300         }
28301         
28302         if(this.dragable){
28303             this.onMouseMove(e);
28304             return;
28305         }
28306         
28307         var coords = [];
28308         
28309         for(var i = 0, finger; finger = touches[i]; i++){
28310             coords.push(finger.pageX, finger.pageY);
28311         }
28312         
28313         var x = Math.pow(coords[0] - coords[2], 2);
28314         var y = Math.pow(coords[1] - coords[3], 2);
28315         
28316         this.endDistance = Math.sqrt(x + y);
28317         
28318         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28319         
28320         if(!this.zoomable()){
28321             this.scale = this.startScale;
28322             return;
28323         }
28324         
28325         this.draw();
28326         
28327     },
28328     
28329     onTouchEnd : function(e)
28330     {
28331         this.pinching = false;
28332         this.dragable = false;
28333         
28334     },
28335     
28336     process : function(file, crop)
28337     {
28338         if(this.loadMask){
28339             this.maskEl.mask(this.loadingText);
28340         }
28341         
28342         this.xhr = new XMLHttpRequest();
28343         
28344         file.xhr = this.xhr;
28345
28346         this.xhr.open(this.method, this.url, true);
28347         
28348         var headers = {
28349             "Accept": "application/json",
28350             "Cache-Control": "no-cache",
28351             "X-Requested-With": "XMLHttpRequest"
28352         };
28353         
28354         for (var headerName in headers) {
28355             var headerValue = headers[headerName];
28356             if (headerValue) {
28357                 this.xhr.setRequestHeader(headerName, headerValue);
28358             }
28359         }
28360         
28361         var _this = this;
28362         
28363         this.xhr.onload = function()
28364         {
28365             _this.xhrOnLoad(_this.xhr);
28366         }
28367         
28368         this.xhr.onerror = function()
28369         {
28370             _this.xhrOnError(_this.xhr);
28371         }
28372         
28373         var formData = new FormData();
28374
28375         formData.append('returnHTML', 'NO');
28376         
28377         if(crop){
28378             formData.append('crop', crop);
28379         }
28380         
28381         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28382             formData.append(this.paramName, file, file.name);
28383         }
28384         
28385         if(typeof(file.filename) != 'undefined'){
28386             formData.append('filename', file.filename);
28387         }
28388         
28389         if(typeof(file.mimetype) != 'undefined'){
28390             formData.append('mimetype', file.mimetype);
28391         }
28392         
28393         if(this.fireEvent('arrange', this, formData) != false){
28394             this.xhr.send(formData);
28395         };
28396     },
28397     
28398     xhrOnLoad : function(xhr)
28399     {
28400         if(this.loadMask){
28401             this.maskEl.unmask();
28402         }
28403         
28404         if (xhr.readyState !== 4) {
28405             this.fireEvent('exception', this, xhr);
28406             return;
28407         }
28408
28409         var response = Roo.decode(xhr.responseText);
28410         
28411         if(!response.success){
28412             this.fireEvent('exception', this, xhr);
28413             return;
28414         }
28415         
28416         var response = Roo.decode(xhr.responseText);
28417         
28418         this.fireEvent('upload', this, response);
28419         
28420     },
28421     
28422     xhrOnError : function()
28423     {
28424         if(this.loadMask){
28425             this.maskEl.unmask();
28426         }
28427         
28428         Roo.log('xhr on error');
28429         
28430         var response = Roo.decode(xhr.responseText);
28431           
28432         Roo.log(response);
28433         
28434     },
28435     
28436     prepare : function(file)
28437     {   
28438         if(this.loadMask){
28439             this.maskEl.mask(this.loadingText);
28440         }
28441         
28442         this.file = false;
28443         this.exif = {};
28444         
28445         if(typeof(file) === 'string'){
28446             this.loadCanvas(file);
28447             return;
28448         }
28449         
28450         if(!file || !this.urlAPI){
28451             return;
28452         }
28453         
28454         this.file = file;
28455         this.cropType = file.type;
28456         
28457         var _this = this;
28458         
28459         if(this.fireEvent('prepare', this, this.file) != false){
28460             
28461             var reader = new FileReader();
28462             
28463             reader.onload = function (e) {
28464                 if (e.target.error) {
28465                     Roo.log(e.target.error);
28466                     return;
28467                 }
28468                 
28469                 var buffer = e.target.result,
28470                     dataView = new DataView(buffer),
28471                     offset = 2,
28472                     maxOffset = dataView.byteLength - 4,
28473                     markerBytes,
28474                     markerLength;
28475                 
28476                 if (dataView.getUint16(0) === 0xffd8) {
28477                     while (offset < maxOffset) {
28478                         markerBytes = dataView.getUint16(offset);
28479                         
28480                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28481                             markerLength = dataView.getUint16(offset + 2) + 2;
28482                             if (offset + markerLength > dataView.byteLength) {
28483                                 Roo.log('Invalid meta data: Invalid segment size.');
28484                                 break;
28485                             }
28486                             
28487                             if(markerBytes == 0xffe1){
28488                                 _this.parseExifData(
28489                                     dataView,
28490                                     offset,
28491                                     markerLength
28492                                 );
28493                             }
28494                             
28495                             offset += markerLength;
28496                             
28497                             continue;
28498                         }
28499                         
28500                         break;
28501                     }
28502                     
28503                 }
28504                 
28505                 var url = _this.urlAPI.createObjectURL(_this.file);
28506                 
28507                 _this.loadCanvas(url);
28508                 
28509                 return;
28510             }
28511             
28512             reader.readAsArrayBuffer(this.file);
28513             
28514         }
28515         
28516     },
28517     
28518     parseExifData : function(dataView, offset, length)
28519     {
28520         var tiffOffset = offset + 10,
28521             littleEndian,
28522             dirOffset;
28523     
28524         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28525             // No Exif data, might be XMP data instead
28526             return;
28527         }
28528         
28529         // Check for the ASCII code for "Exif" (0x45786966):
28530         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28531             // No Exif data, might be XMP data instead
28532             return;
28533         }
28534         if (tiffOffset + 8 > dataView.byteLength) {
28535             Roo.log('Invalid Exif data: Invalid segment size.');
28536             return;
28537         }
28538         // Check for the two null bytes:
28539         if (dataView.getUint16(offset + 8) !== 0x0000) {
28540             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28541             return;
28542         }
28543         // Check the byte alignment:
28544         switch (dataView.getUint16(tiffOffset)) {
28545         case 0x4949:
28546             littleEndian = true;
28547             break;
28548         case 0x4D4D:
28549             littleEndian = false;
28550             break;
28551         default:
28552             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28553             return;
28554         }
28555         // Check for the TIFF tag marker (0x002A):
28556         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28557             Roo.log('Invalid Exif data: Missing TIFF marker.');
28558             return;
28559         }
28560         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28561         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28562         
28563         this.parseExifTags(
28564             dataView,
28565             tiffOffset,
28566             tiffOffset + dirOffset,
28567             littleEndian
28568         );
28569     },
28570     
28571     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28572     {
28573         var tagsNumber,
28574             dirEndOffset,
28575             i;
28576         if (dirOffset + 6 > dataView.byteLength) {
28577             Roo.log('Invalid Exif data: Invalid directory offset.');
28578             return;
28579         }
28580         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28581         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28582         if (dirEndOffset + 4 > dataView.byteLength) {
28583             Roo.log('Invalid Exif data: Invalid directory size.');
28584             return;
28585         }
28586         for (i = 0; i < tagsNumber; i += 1) {
28587             this.parseExifTag(
28588                 dataView,
28589                 tiffOffset,
28590                 dirOffset + 2 + 12 * i, // tag offset
28591                 littleEndian
28592             );
28593         }
28594         // Return the offset to the next directory:
28595         return dataView.getUint32(dirEndOffset, littleEndian);
28596     },
28597     
28598     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28599     {
28600         var tag = dataView.getUint16(offset, littleEndian);
28601         
28602         this.exif[tag] = this.getExifValue(
28603             dataView,
28604             tiffOffset,
28605             offset,
28606             dataView.getUint16(offset + 2, littleEndian), // tag type
28607             dataView.getUint32(offset + 4, littleEndian), // tag length
28608             littleEndian
28609         );
28610     },
28611     
28612     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28613     {
28614         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28615             tagSize,
28616             dataOffset,
28617             values,
28618             i,
28619             str,
28620             c;
28621     
28622         if (!tagType) {
28623             Roo.log('Invalid Exif data: Invalid tag type.');
28624             return;
28625         }
28626         
28627         tagSize = tagType.size * length;
28628         // Determine if the value is contained in the dataOffset bytes,
28629         // or if the value at the dataOffset is a pointer to the actual data:
28630         dataOffset = tagSize > 4 ?
28631                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28632         if (dataOffset + tagSize > dataView.byteLength) {
28633             Roo.log('Invalid Exif data: Invalid data offset.');
28634             return;
28635         }
28636         if (length === 1) {
28637             return tagType.getValue(dataView, dataOffset, littleEndian);
28638         }
28639         values = [];
28640         for (i = 0; i < length; i += 1) {
28641             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28642         }
28643         
28644         if (tagType.ascii) {
28645             str = '';
28646             // Concatenate the chars:
28647             for (i = 0; i < values.length; i += 1) {
28648                 c = values[i];
28649                 // Ignore the terminating NULL byte(s):
28650                 if (c === '\u0000') {
28651                     break;
28652                 }
28653                 str += c;
28654             }
28655             return str;
28656         }
28657         return values;
28658     }
28659     
28660 });
28661
28662 Roo.apply(Roo.bootstrap.UploadCropbox, {
28663     tags : {
28664         'Orientation': 0x0112
28665     },
28666     
28667     Orientation: {
28668             1: 0, //'top-left',
28669 //            2: 'top-right',
28670             3: 180, //'bottom-right',
28671 //            4: 'bottom-left',
28672 //            5: 'left-top',
28673             6: 90, //'right-top',
28674 //            7: 'right-bottom',
28675             8: 270 //'left-bottom'
28676     },
28677     
28678     exifTagTypes : {
28679         // byte, 8-bit unsigned int:
28680         1: {
28681             getValue: function (dataView, dataOffset) {
28682                 return dataView.getUint8(dataOffset);
28683             },
28684             size: 1
28685         },
28686         // ascii, 8-bit byte:
28687         2: {
28688             getValue: function (dataView, dataOffset) {
28689                 return String.fromCharCode(dataView.getUint8(dataOffset));
28690             },
28691             size: 1,
28692             ascii: true
28693         },
28694         // short, 16 bit int:
28695         3: {
28696             getValue: function (dataView, dataOffset, littleEndian) {
28697                 return dataView.getUint16(dataOffset, littleEndian);
28698             },
28699             size: 2
28700         },
28701         // long, 32 bit int:
28702         4: {
28703             getValue: function (dataView, dataOffset, littleEndian) {
28704                 return dataView.getUint32(dataOffset, littleEndian);
28705             },
28706             size: 4
28707         },
28708         // rational = two long values, first is numerator, second is denominator:
28709         5: {
28710             getValue: function (dataView, dataOffset, littleEndian) {
28711                 return dataView.getUint32(dataOffset, littleEndian) /
28712                     dataView.getUint32(dataOffset + 4, littleEndian);
28713             },
28714             size: 8
28715         },
28716         // slong, 32 bit signed int:
28717         9: {
28718             getValue: function (dataView, dataOffset, littleEndian) {
28719                 return dataView.getInt32(dataOffset, littleEndian);
28720             },
28721             size: 4
28722         },
28723         // srational, two slongs, first is numerator, second is denominator:
28724         10: {
28725             getValue: function (dataView, dataOffset, littleEndian) {
28726                 return dataView.getInt32(dataOffset, littleEndian) /
28727                     dataView.getInt32(dataOffset + 4, littleEndian);
28728             },
28729             size: 8
28730         }
28731     },
28732     
28733     footer : {
28734         STANDARD : [
28735             {
28736                 tag : 'div',
28737                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28738                 action : 'rotate-left',
28739                 cn : [
28740                     {
28741                         tag : 'button',
28742                         cls : 'btn btn-default',
28743                         html : '<i class="fa fa-undo"></i>'
28744                     }
28745                 ]
28746             },
28747             {
28748                 tag : 'div',
28749                 cls : 'btn-group roo-upload-cropbox-picture',
28750                 action : 'picture',
28751                 cn : [
28752                     {
28753                         tag : 'button',
28754                         cls : 'btn btn-default',
28755                         html : '<i class="fa fa-picture-o"></i>'
28756                     }
28757                 ]
28758             },
28759             {
28760                 tag : 'div',
28761                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28762                 action : 'rotate-right',
28763                 cn : [
28764                     {
28765                         tag : 'button',
28766                         cls : 'btn btn-default',
28767                         html : '<i class="fa fa-repeat"></i>'
28768                     }
28769                 ]
28770             }
28771         ],
28772         DOCUMENT : [
28773             {
28774                 tag : 'div',
28775                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28776                 action : 'rotate-left',
28777                 cn : [
28778                     {
28779                         tag : 'button',
28780                         cls : 'btn btn-default',
28781                         html : '<i class="fa fa-undo"></i>'
28782                     }
28783                 ]
28784             },
28785             {
28786                 tag : 'div',
28787                 cls : 'btn-group roo-upload-cropbox-download',
28788                 action : 'download',
28789                 cn : [
28790                     {
28791                         tag : 'button',
28792                         cls : 'btn btn-default',
28793                         html : '<i class="fa fa-download"></i>'
28794                     }
28795                 ]
28796             },
28797             {
28798                 tag : 'div',
28799                 cls : 'btn-group roo-upload-cropbox-crop',
28800                 action : 'crop',
28801                 cn : [
28802                     {
28803                         tag : 'button',
28804                         cls : 'btn btn-default',
28805                         html : '<i class="fa fa-crop"></i>'
28806                     }
28807                 ]
28808             },
28809             {
28810                 tag : 'div',
28811                 cls : 'btn-group roo-upload-cropbox-trash',
28812                 action : 'trash',
28813                 cn : [
28814                     {
28815                         tag : 'button',
28816                         cls : 'btn btn-default',
28817                         html : '<i class="fa fa-trash"></i>'
28818                     }
28819                 ]
28820             },
28821             {
28822                 tag : 'div',
28823                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28824                 action : 'rotate-right',
28825                 cn : [
28826                     {
28827                         tag : 'button',
28828                         cls : 'btn btn-default',
28829                         html : '<i class="fa fa-repeat"></i>'
28830                     }
28831                 ]
28832             }
28833         ],
28834         ROTATOR : [
28835             {
28836                 tag : 'div',
28837                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28838                 action : 'rotate-left',
28839                 cn : [
28840                     {
28841                         tag : 'button',
28842                         cls : 'btn btn-default',
28843                         html : '<i class="fa fa-undo"></i>'
28844                     }
28845                 ]
28846             },
28847             {
28848                 tag : 'div',
28849                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28850                 action : 'rotate-right',
28851                 cn : [
28852                     {
28853                         tag : 'button',
28854                         cls : 'btn btn-default',
28855                         html : '<i class="fa fa-repeat"></i>'
28856                     }
28857                 ]
28858             }
28859         ]
28860     }
28861 });
28862
28863 /*
28864 * Licence: LGPL
28865 */
28866
28867 /**
28868  * @class Roo.bootstrap.DocumentManager
28869  * @extends Roo.bootstrap.Component
28870  * Bootstrap DocumentManager class
28871  * @cfg {String} paramName default 'imageUpload'
28872  * @cfg {String} toolTipName default 'filename'
28873  * @cfg {String} method default POST
28874  * @cfg {String} url action url
28875  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28876  * @cfg {Boolean} multiple multiple upload default true
28877  * @cfg {Number} thumbSize default 300
28878  * @cfg {String} fieldLabel
28879  * @cfg {Number} labelWidth default 4
28880  * @cfg {String} labelAlign (left|top) default left
28881  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28882 * @cfg {Number} labellg set the width of label (1-12)
28883  * @cfg {Number} labelmd set the width of label (1-12)
28884  * @cfg {Number} labelsm set the width of label (1-12)
28885  * @cfg {Number} labelxs set the width of label (1-12)
28886  * 
28887  * @constructor
28888  * Create a new DocumentManager
28889  * @param {Object} config The config object
28890  */
28891
28892 Roo.bootstrap.DocumentManager = function(config){
28893     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28894     
28895     this.files = [];
28896     this.delegates = [];
28897     
28898     this.addEvents({
28899         /**
28900          * @event initial
28901          * Fire when initial the DocumentManager
28902          * @param {Roo.bootstrap.DocumentManager} this
28903          */
28904         "initial" : true,
28905         /**
28906          * @event inspect
28907          * inspect selected file
28908          * @param {Roo.bootstrap.DocumentManager} this
28909          * @param {File} file
28910          */
28911         "inspect" : true,
28912         /**
28913          * @event exception
28914          * Fire when xhr load exception
28915          * @param {Roo.bootstrap.DocumentManager} this
28916          * @param {XMLHttpRequest} xhr
28917          */
28918         "exception" : true,
28919         /**
28920          * @event afterupload
28921          * Fire when xhr load exception
28922          * @param {Roo.bootstrap.DocumentManager} this
28923          * @param {XMLHttpRequest} xhr
28924          */
28925         "afterupload" : true,
28926         /**
28927          * @event prepare
28928          * prepare the form data
28929          * @param {Roo.bootstrap.DocumentManager} this
28930          * @param {Object} formData
28931          */
28932         "prepare" : true,
28933         /**
28934          * @event remove
28935          * Fire when remove the file
28936          * @param {Roo.bootstrap.DocumentManager} this
28937          * @param {Object} file
28938          */
28939         "remove" : true,
28940         /**
28941          * @event refresh
28942          * Fire after refresh the file
28943          * @param {Roo.bootstrap.DocumentManager} this
28944          */
28945         "refresh" : true,
28946         /**
28947          * @event click
28948          * Fire after click the image
28949          * @param {Roo.bootstrap.DocumentManager} this
28950          * @param {Object} file
28951          */
28952         "click" : true,
28953         /**
28954          * @event edit
28955          * Fire when upload a image and editable set to true
28956          * @param {Roo.bootstrap.DocumentManager} this
28957          * @param {Object} file
28958          */
28959         "edit" : true,
28960         /**
28961          * @event beforeselectfile
28962          * Fire before select file
28963          * @param {Roo.bootstrap.DocumentManager} this
28964          */
28965         "beforeselectfile" : true,
28966         /**
28967          * @event process
28968          * Fire before process file
28969          * @param {Roo.bootstrap.DocumentManager} this
28970          * @param {Object} file
28971          */
28972         "process" : true,
28973         /**
28974          * @event previewrendered
28975          * Fire when preview rendered
28976          * @param {Roo.bootstrap.DocumentManager} this
28977          * @param {Object} file
28978          */
28979         "previewrendered" : true,
28980         /**
28981          */
28982         "previewResize" : true
28983         
28984     });
28985 };
28986
28987 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28988     
28989     boxes : 0,
28990     inputName : '',
28991     thumbSize : 300,
28992     multiple : true,
28993     files : false,
28994     method : 'POST',
28995     url : '',
28996     paramName : 'imageUpload',
28997     toolTipName : 'filename',
28998     fieldLabel : '',
28999     labelWidth : 4,
29000     labelAlign : 'left',
29001     editable : true,
29002     delegates : false,
29003     xhr : false, 
29004     
29005     labellg : 0,
29006     labelmd : 0,
29007     labelsm : 0,
29008     labelxs : 0,
29009     
29010     getAutoCreate : function()
29011     {   
29012         var managerWidget = {
29013             tag : 'div',
29014             cls : 'roo-document-manager',
29015             cn : [
29016                 {
29017                     tag : 'input',
29018                     cls : 'roo-document-manager-selector',
29019                     type : 'file'
29020                 },
29021                 {
29022                     tag : 'div',
29023                     cls : 'roo-document-manager-uploader',
29024                     cn : [
29025                         {
29026                             tag : 'div',
29027                             cls : 'roo-document-manager-upload-btn',
29028                             html : '<i class="fa fa-plus"></i>'
29029                         }
29030                     ]
29031                     
29032                 }
29033             ]
29034         };
29035         
29036         var content = [
29037             {
29038                 tag : 'div',
29039                 cls : 'column col-md-12',
29040                 cn : managerWidget
29041             }
29042         ];
29043         
29044         if(this.fieldLabel.length){
29045             
29046             content = [
29047                 {
29048                     tag : 'div',
29049                     cls : 'column col-md-12',
29050                     html : this.fieldLabel
29051                 },
29052                 {
29053                     tag : 'div',
29054                     cls : 'column col-md-12',
29055                     cn : managerWidget
29056                 }
29057             ];
29058
29059             if(this.labelAlign == 'left'){
29060                 content = [
29061                     {
29062                         tag : 'div',
29063                         cls : 'column',
29064                         html : this.fieldLabel
29065                     },
29066                     {
29067                         tag : 'div',
29068                         cls : 'column',
29069                         cn : managerWidget
29070                     }
29071                 ];
29072                 
29073                 if(this.labelWidth > 12){
29074                     content[0].style = "width: " + this.labelWidth + 'px';
29075                 }
29076
29077                 if(this.labelWidth < 13 && this.labelmd == 0){
29078                     this.labelmd = this.labelWidth;
29079                 }
29080
29081                 if(this.labellg > 0){
29082                     content[0].cls += ' col-lg-' + this.labellg;
29083                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29084                 }
29085
29086                 if(this.labelmd > 0){
29087                     content[0].cls += ' col-md-' + this.labelmd;
29088                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29089                 }
29090
29091                 if(this.labelsm > 0){
29092                     content[0].cls += ' col-sm-' + this.labelsm;
29093                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29094                 }
29095
29096                 if(this.labelxs > 0){
29097                     content[0].cls += ' col-xs-' + this.labelxs;
29098                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29099                 }
29100                 
29101             }
29102         }
29103         
29104         var cfg = {
29105             tag : 'div',
29106             cls : 'row clearfix',
29107             cn : content
29108         };
29109         
29110         return cfg;
29111         
29112     },
29113     
29114     initEvents : function()
29115     {
29116         this.managerEl = this.el.select('.roo-document-manager', true).first();
29117         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29118         
29119         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29120         this.selectorEl.hide();
29121         
29122         if(this.multiple){
29123             this.selectorEl.attr('multiple', 'multiple');
29124         }
29125         
29126         this.selectorEl.on('change', this.onFileSelected, this);
29127         
29128         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29129         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29130         
29131         this.uploader.on('click', this.onUploaderClick, this);
29132         
29133         this.renderProgressDialog();
29134         
29135         var _this = this;
29136         
29137         window.addEventListener("resize", function() { _this.refresh(); } );
29138         
29139         this.fireEvent('initial', this);
29140     },
29141     
29142     renderProgressDialog : function()
29143     {
29144         var _this = this;
29145         
29146         this.progressDialog = new Roo.bootstrap.Modal({
29147             cls : 'roo-document-manager-progress-dialog',
29148             allow_close : false,
29149             title : '',
29150             buttons : [
29151                 {
29152                     name  :'cancel',
29153                     weight : 'danger',
29154                     html : 'Cancel'
29155                 }
29156             ], 
29157             listeners : { 
29158                 btnclick : function() {
29159                     _this.uploadCancel();
29160                     this.hide();
29161                 }
29162             }
29163         });
29164          
29165         this.progressDialog.render(Roo.get(document.body));
29166          
29167         this.progress = new Roo.bootstrap.Progress({
29168             cls : 'roo-document-manager-progress',
29169             active : true,
29170             striped : true
29171         });
29172         
29173         this.progress.render(this.progressDialog.getChildContainer());
29174         
29175         this.progressBar = new Roo.bootstrap.ProgressBar({
29176             cls : 'roo-document-manager-progress-bar',
29177             aria_valuenow : 0,
29178             aria_valuemin : 0,
29179             aria_valuemax : 12,
29180             panel : 'success'
29181         });
29182         
29183         this.progressBar.render(this.progress.getChildContainer());
29184     },
29185     
29186     onUploaderClick : function(e)
29187     {
29188         e.preventDefault();
29189      
29190         if(this.fireEvent('beforeselectfile', this) != false){
29191             this.selectorEl.dom.click();
29192         }
29193         
29194     },
29195     
29196     onFileSelected : function(e)
29197     {
29198         e.preventDefault();
29199         
29200         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29201             return;
29202         }
29203         
29204         Roo.each(this.selectorEl.dom.files, function(file){
29205             if(this.fireEvent('inspect', this, file) != false){
29206                 this.files.push(file);
29207             }
29208         }, this);
29209         
29210         this.queue();
29211         
29212     },
29213     
29214     queue : function()
29215     {
29216         this.selectorEl.dom.value = '';
29217         
29218         if(!this.files || !this.files.length){
29219             return;
29220         }
29221         
29222         if(this.boxes > 0 && this.files.length > this.boxes){
29223             this.files = this.files.slice(0, this.boxes);
29224         }
29225         
29226         this.uploader.show();
29227         
29228         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29229             this.uploader.hide();
29230         }
29231         
29232         var _this = this;
29233         
29234         var files = [];
29235         
29236         var docs = [];
29237         
29238         Roo.each(this.files, function(file){
29239             
29240             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29241                 var f = this.renderPreview(file);
29242                 files.push(f);
29243                 return;
29244             }
29245             
29246             if(file.type.indexOf('image') != -1){
29247                 this.delegates.push(
29248                     (function(){
29249                         _this.process(file);
29250                     }).createDelegate(this)
29251                 );
29252         
29253                 return;
29254             }
29255             
29256             docs.push(
29257                 (function(){
29258                     _this.process(file);
29259                 }).createDelegate(this)
29260             );
29261             
29262         }, this);
29263         
29264         this.files = files;
29265         
29266         this.delegates = this.delegates.concat(docs);
29267         
29268         if(!this.delegates.length){
29269             this.refresh();
29270             return;
29271         }
29272         
29273         this.progressBar.aria_valuemax = this.delegates.length;
29274         
29275         this.arrange();
29276         
29277         return;
29278     },
29279     
29280     arrange : function()
29281     {
29282         if(!this.delegates.length){
29283             this.progressDialog.hide();
29284             this.refresh();
29285             return;
29286         }
29287         
29288         var delegate = this.delegates.shift();
29289         
29290         this.progressDialog.show();
29291         
29292         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29293         
29294         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29295         
29296         delegate();
29297     },
29298     
29299     refresh : function()
29300     {
29301         this.uploader.show();
29302         
29303         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29304             this.uploader.hide();
29305         }
29306         
29307         Roo.isTouch ? this.closable(false) : this.closable(true);
29308         
29309         this.fireEvent('refresh', this);
29310     },
29311     
29312     onRemove : function(e, el, o)
29313     {
29314         e.preventDefault();
29315         
29316         this.fireEvent('remove', this, o);
29317         
29318     },
29319     
29320     remove : function(o)
29321     {
29322         var files = [];
29323         
29324         Roo.each(this.files, function(file){
29325             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29326                 files.push(file);
29327                 return;
29328             }
29329
29330             o.target.remove();
29331
29332         }, this);
29333         
29334         this.files = files;
29335         
29336         this.refresh();
29337     },
29338     
29339     clear : function()
29340     {
29341         Roo.each(this.files, function(file){
29342             if(!file.target){
29343                 return;
29344             }
29345             
29346             file.target.remove();
29347
29348         }, this);
29349         
29350         this.files = [];
29351         
29352         this.refresh();
29353     },
29354     
29355     onClick : function(e, el, o)
29356     {
29357         e.preventDefault();
29358         
29359         this.fireEvent('click', this, o);
29360         
29361     },
29362     
29363     closable : function(closable)
29364     {
29365         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29366             
29367             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29368             
29369             if(closable){
29370                 el.show();
29371                 return;
29372             }
29373             
29374             el.hide();
29375             
29376         }, this);
29377     },
29378     
29379     xhrOnLoad : function(xhr)
29380     {
29381         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29382             el.remove();
29383         }, this);
29384         
29385         if (xhr.readyState !== 4) {
29386             this.arrange();
29387             this.fireEvent('exception', this, xhr);
29388             return;
29389         }
29390
29391         var response = Roo.decode(xhr.responseText);
29392         
29393         if(!response.success){
29394             this.arrange();
29395             this.fireEvent('exception', this, xhr);
29396             return;
29397         }
29398         
29399         var file = this.renderPreview(response.data);
29400         
29401         this.files.push(file);
29402         
29403         this.arrange();
29404         
29405         this.fireEvent('afterupload', this, xhr);
29406         
29407     },
29408     
29409     xhrOnError : function(xhr)
29410     {
29411         Roo.log('xhr on error');
29412         
29413         var response = Roo.decode(xhr.responseText);
29414           
29415         Roo.log(response);
29416         
29417         this.arrange();
29418     },
29419     
29420     process : function(file)
29421     {
29422         if(this.fireEvent('process', this, file) !== false){
29423             if(this.editable && file.type.indexOf('image') != -1){
29424                 this.fireEvent('edit', this, file);
29425                 return;
29426             }
29427
29428             this.uploadStart(file, false);
29429
29430             return;
29431         }
29432         
29433     },
29434     
29435     uploadStart : function(file, crop)
29436     {
29437         this.xhr = new XMLHttpRequest();
29438         
29439         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29440             this.arrange();
29441             return;
29442         }
29443         
29444         file.xhr = this.xhr;
29445             
29446         this.managerEl.createChild({
29447             tag : 'div',
29448             cls : 'roo-document-manager-loading',
29449             cn : [
29450                 {
29451                     tag : 'div',
29452                     tooltip : file.name,
29453                     cls : 'roo-document-manager-thumb',
29454                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29455                 }
29456             ]
29457
29458         });
29459
29460         this.xhr.open(this.method, this.url, true);
29461         
29462         var headers = {
29463             "Accept": "application/json",
29464             "Cache-Control": "no-cache",
29465             "X-Requested-With": "XMLHttpRequest"
29466         };
29467         
29468         for (var headerName in headers) {
29469             var headerValue = headers[headerName];
29470             if (headerValue) {
29471                 this.xhr.setRequestHeader(headerName, headerValue);
29472             }
29473         }
29474         
29475         var _this = this;
29476         
29477         this.xhr.onload = function()
29478         {
29479             _this.xhrOnLoad(_this.xhr);
29480         }
29481         
29482         this.xhr.onerror = function()
29483         {
29484             _this.xhrOnError(_this.xhr);
29485         }
29486         
29487         var formData = new FormData();
29488
29489         formData.append('returnHTML', 'NO');
29490         
29491         if(crop){
29492             formData.append('crop', crop);
29493         }
29494         
29495         formData.append(this.paramName, file, file.name);
29496         
29497         var options = {
29498             file : file, 
29499             manually : false
29500         };
29501         
29502         if(this.fireEvent('prepare', this, formData, options) != false){
29503             
29504             if(options.manually){
29505                 return;
29506             }
29507             
29508             this.xhr.send(formData);
29509             return;
29510         };
29511         
29512         this.uploadCancel();
29513     },
29514     
29515     uploadCancel : function()
29516     {
29517         if (this.xhr) {
29518             this.xhr.abort();
29519         }
29520         
29521         this.delegates = [];
29522         
29523         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29524             el.remove();
29525         }, this);
29526         
29527         this.arrange();
29528     },
29529     
29530     renderPreview : function(file)
29531     {
29532         if(typeof(file.target) != 'undefined' && file.target){
29533             return file;
29534         }
29535         
29536         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29537         
29538         var previewEl = this.managerEl.createChild({
29539             tag : 'div',
29540             cls : 'roo-document-manager-preview',
29541             cn : [
29542                 {
29543                     tag : 'div',
29544                     tooltip : file[this.toolTipName],
29545                     cls : 'roo-document-manager-thumb',
29546                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29547                 },
29548                 {
29549                     tag : 'button',
29550                     cls : 'close',
29551                     html : '<i class="fa fa-times-circle"></i>'
29552                 }
29553             ]
29554         });
29555
29556         var close = previewEl.select('button.close', true).first();
29557
29558         close.on('click', this.onRemove, this, file);
29559
29560         file.target = previewEl;
29561
29562         var image = previewEl.select('img', true).first();
29563         
29564         var _this = this;
29565         
29566         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29567         
29568         image.on('click', this.onClick, this, file);
29569         
29570         this.fireEvent('previewrendered', this, file);
29571         
29572         return file;
29573         
29574     },
29575     
29576     onPreviewLoad : function(file, image)
29577     {
29578         if(typeof(file.target) == 'undefined' || !file.target){
29579             return;
29580         }
29581         
29582         var width = image.dom.naturalWidth || image.dom.width;
29583         var height = image.dom.naturalHeight || image.dom.height;
29584         
29585         if(!this.previewResize) {
29586             return;
29587         }
29588         
29589         if(width > height){
29590             file.target.addClass('wide');
29591             return;
29592         }
29593         
29594         file.target.addClass('tall');
29595         return;
29596         
29597     },
29598     
29599     uploadFromSource : function(file, crop)
29600     {
29601         this.xhr = new XMLHttpRequest();
29602         
29603         this.managerEl.createChild({
29604             tag : 'div',
29605             cls : 'roo-document-manager-loading',
29606             cn : [
29607                 {
29608                     tag : 'div',
29609                     tooltip : file.name,
29610                     cls : 'roo-document-manager-thumb',
29611                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29612                 }
29613             ]
29614
29615         });
29616
29617         this.xhr.open(this.method, this.url, true);
29618         
29619         var headers = {
29620             "Accept": "application/json",
29621             "Cache-Control": "no-cache",
29622             "X-Requested-With": "XMLHttpRequest"
29623         };
29624         
29625         for (var headerName in headers) {
29626             var headerValue = headers[headerName];
29627             if (headerValue) {
29628                 this.xhr.setRequestHeader(headerName, headerValue);
29629             }
29630         }
29631         
29632         var _this = this;
29633         
29634         this.xhr.onload = function()
29635         {
29636             _this.xhrOnLoad(_this.xhr);
29637         }
29638         
29639         this.xhr.onerror = function()
29640         {
29641             _this.xhrOnError(_this.xhr);
29642         }
29643         
29644         var formData = new FormData();
29645
29646         formData.append('returnHTML', 'NO');
29647         
29648         formData.append('crop', crop);
29649         
29650         if(typeof(file.filename) != 'undefined'){
29651             formData.append('filename', file.filename);
29652         }
29653         
29654         if(typeof(file.mimetype) != 'undefined'){
29655             formData.append('mimetype', file.mimetype);
29656         }
29657         
29658         Roo.log(formData);
29659         
29660         if(this.fireEvent('prepare', this, formData) != false){
29661             this.xhr.send(formData);
29662         };
29663     }
29664 });
29665
29666 /*
29667 * Licence: LGPL
29668 */
29669
29670 /**
29671  * @class Roo.bootstrap.DocumentViewer
29672  * @extends Roo.bootstrap.Component
29673  * Bootstrap DocumentViewer class
29674  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29675  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29676  * 
29677  * @constructor
29678  * Create a new DocumentViewer
29679  * @param {Object} config The config object
29680  */
29681
29682 Roo.bootstrap.DocumentViewer = function(config){
29683     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29684     
29685     this.addEvents({
29686         /**
29687          * @event initial
29688          * Fire after initEvent
29689          * @param {Roo.bootstrap.DocumentViewer} this
29690          */
29691         "initial" : true,
29692         /**
29693          * @event click
29694          * Fire after click
29695          * @param {Roo.bootstrap.DocumentViewer} this
29696          */
29697         "click" : true,
29698         /**
29699          * @event download
29700          * Fire after download button
29701          * @param {Roo.bootstrap.DocumentViewer} this
29702          */
29703         "download" : true,
29704         /**
29705          * @event trash
29706          * Fire after trash button
29707          * @param {Roo.bootstrap.DocumentViewer} this
29708          */
29709         "trash" : true
29710         
29711     });
29712 };
29713
29714 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29715     
29716     showDownload : true,
29717     
29718     showTrash : true,
29719     
29720     getAutoCreate : function()
29721     {
29722         var cfg = {
29723             tag : 'div',
29724             cls : 'roo-document-viewer',
29725             cn : [
29726                 {
29727                     tag : 'div',
29728                     cls : 'roo-document-viewer-body',
29729                     cn : [
29730                         {
29731                             tag : 'div',
29732                             cls : 'roo-document-viewer-thumb',
29733                             cn : [
29734                                 {
29735                                     tag : 'img',
29736                                     cls : 'roo-document-viewer-image'
29737                                 }
29738                             ]
29739                         }
29740                     ]
29741                 },
29742                 {
29743                     tag : 'div',
29744                     cls : 'roo-document-viewer-footer',
29745                     cn : {
29746                         tag : 'div',
29747                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29748                         cn : [
29749                             {
29750                                 tag : 'div',
29751                                 cls : 'btn-group roo-document-viewer-download',
29752                                 cn : [
29753                                     {
29754                                         tag : 'button',
29755                                         cls : 'btn btn-default',
29756                                         html : '<i class="fa fa-download"></i>'
29757                                     }
29758                                 ]
29759                             },
29760                             {
29761                                 tag : 'div',
29762                                 cls : 'btn-group roo-document-viewer-trash',
29763                                 cn : [
29764                                     {
29765                                         tag : 'button',
29766                                         cls : 'btn btn-default',
29767                                         html : '<i class="fa fa-trash"></i>'
29768                                     }
29769                                 ]
29770                             }
29771                         ]
29772                     }
29773                 }
29774             ]
29775         };
29776         
29777         return cfg;
29778     },
29779     
29780     initEvents : function()
29781     {
29782         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29783         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29784         
29785         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29786         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29787         
29788         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29789         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29790         
29791         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29792         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29793         
29794         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29795         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29796         
29797         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29798         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29799         
29800         this.bodyEl.on('click', this.onClick, this);
29801         this.downloadBtn.on('click', this.onDownload, this);
29802         this.trashBtn.on('click', this.onTrash, this);
29803         
29804         this.downloadBtn.hide();
29805         this.trashBtn.hide();
29806         
29807         if(this.showDownload){
29808             this.downloadBtn.show();
29809         }
29810         
29811         if(this.showTrash){
29812             this.trashBtn.show();
29813         }
29814         
29815         if(!this.showDownload && !this.showTrash) {
29816             this.footerEl.hide();
29817         }
29818         
29819     },
29820     
29821     initial : function()
29822     {
29823         this.fireEvent('initial', this);
29824         
29825     },
29826     
29827     onClick : function(e)
29828     {
29829         e.preventDefault();
29830         
29831         this.fireEvent('click', this);
29832     },
29833     
29834     onDownload : function(e)
29835     {
29836         e.preventDefault();
29837         
29838         this.fireEvent('download', this);
29839     },
29840     
29841     onTrash : function(e)
29842     {
29843         e.preventDefault();
29844         
29845         this.fireEvent('trash', this);
29846     }
29847     
29848 });
29849 /*
29850  * - LGPL
29851  *
29852  * nav progress bar
29853  * 
29854  */
29855
29856 /**
29857  * @class Roo.bootstrap.NavProgressBar
29858  * @extends Roo.bootstrap.Component
29859  * Bootstrap NavProgressBar class
29860  * 
29861  * @constructor
29862  * Create a new nav progress bar
29863  * @param {Object} config The config object
29864  */
29865
29866 Roo.bootstrap.NavProgressBar = function(config){
29867     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29868
29869     this.bullets = this.bullets || [];
29870    
29871 //    Roo.bootstrap.NavProgressBar.register(this);
29872      this.addEvents({
29873         /**
29874              * @event changed
29875              * Fires when the active item changes
29876              * @param {Roo.bootstrap.NavProgressBar} this
29877              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29878              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29879          */
29880         'changed': true
29881      });
29882     
29883 };
29884
29885 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29886     
29887     bullets : [],
29888     barItems : [],
29889     
29890     getAutoCreate : function()
29891     {
29892         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29893         
29894         cfg = {
29895             tag : 'div',
29896             cls : 'roo-navigation-bar-group',
29897             cn : [
29898                 {
29899                     tag : 'div',
29900                     cls : 'roo-navigation-top-bar'
29901                 },
29902                 {
29903                     tag : 'div',
29904                     cls : 'roo-navigation-bullets-bar',
29905                     cn : [
29906                         {
29907                             tag : 'ul',
29908                             cls : 'roo-navigation-bar'
29909                         }
29910                     ]
29911                 },
29912                 
29913                 {
29914                     tag : 'div',
29915                     cls : 'roo-navigation-bottom-bar'
29916                 }
29917             ]
29918             
29919         };
29920         
29921         return cfg;
29922         
29923     },
29924     
29925     initEvents: function() 
29926     {
29927         
29928     },
29929     
29930     onRender : function(ct, position) 
29931     {
29932         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29933         
29934         if(this.bullets.length){
29935             Roo.each(this.bullets, function(b){
29936                this.addItem(b);
29937             }, this);
29938         }
29939         
29940         this.format();
29941         
29942     },
29943     
29944     addItem : function(cfg)
29945     {
29946         var item = new Roo.bootstrap.NavProgressItem(cfg);
29947         
29948         item.parentId = this.id;
29949         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29950         
29951         if(cfg.html){
29952             var top = new Roo.bootstrap.Element({
29953                 tag : 'div',
29954                 cls : 'roo-navigation-bar-text'
29955             });
29956             
29957             var bottom = new Roo.bootstrap.Element({
29958                 tag : 'div',
29959                 cls : 'roo-navigation-bar-text'
29960             });
29961             
29962             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29963             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29964             
29965             var topText = new Roo.bootstrap.Element({
29966                 tag : 'span',
29967                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29968             });
29969             
29970             var bottomText = new Roo.bootstrap.Element({
29971                 tag : 'span',
29972                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29973             });
29974             
29975             topText.onRender(top.el, null);
29976             bottomText.onRender(bottom.el, null);
29977             
29978             item.topEl = top;
29979             item.bottomEl = bottom;
29980         }
29981         
29982         this.barItems.push(item);
29983         
29984         return item;
29985     },
29986     
29987     getActive : function()
29988     {
29989         var active = false;
29990         
29991         Roo.each(this.barItems, function(v){
29992             
29993             if (!v.isActive()) {
29994                 return;
29995             }
29996             
29997             active = v;
29998             return false;
29999             
30000         });
30001         
30002         return active;
30003     },
30004     
30005     setActiveItem : function(item)
30006     {
30007         var prev = false;
30008         
30009         Roo.each(this.barItems, function(v){
30010             if (v.rid == item.rid) {
30011                 return ;
30012             }
30013             
30014             if (v.isActive()) {
30015                 v.setActive(false);
30016                 prev = v;
30017             }
30018         });
30019
30020         item.setActive(true);
30021         
30022         this.fireEvent('changed', this, item, prev);
30023     },
30024     
30025     getBarItem: function(rid)
30026     {
30027         var ret = false;
30028         
30029         Roo.each(this.barItems, function(e) {
30030             if (e.rid != rid) {
30031                 return;
30032             }
30033             
30034             ret =  e;
30035             return false;
30036         });
30037         
30038         return ret;
30039     },
30040     
30041     indexOfItem : function(item)
30042     {
30043         var index = false;
30044         
30045         Roo.each(this.barItems, function(v, i){
30046             
30047             if (v.rid != item.rid) {
30048                 return;
30049             }
30050             
30051             index = i;
30052             return false
30053         });
30054         
30055         return index;
30056     },
30057     
30058     setActiveNext : function()
30059     {
30060         var i = this.indexOfItem(this.getActive());
30061         
30062         if (i > this.barItems.length) {
30063             return;
30064         }
30065         
30066         this.setActiveItem(this.barItems[i+1]);
30067     },
30068     
30069     setActivePrev : function()
30070     {
30071         var i = this.indexOfItem(this.getActive());
30072         
30073         if (i  < 1) {
30074             return;
30075         }
30076         
30077         this.setActiveItem(this.barItems[i-1]);
30078     },
30079     
30080     format : function()
30081     {
30082         if(!this.barItems.length){
30083             return;
30084         }
30085      
30086         var width = 100 / this.barItems.length;
30087         
30088         Roo.each(this.barItems, function(i){
30089             i.el.setStyle('width', width + '%');
30090             i.topEl.el.setStyle('width', width + '%');
30091             i.bottomEl.el.setStyle('width', width + '%');
30092         }, this);
30093         
30094     }
30095     
30096 });
30097 /*
30098  * - LGPL
30099  *
30100  * Nav Progress Item
30101  * 
30102  */
30103
30104 /**
30105  * @class Roo.bootstrap.NavProgressItem
30106  * @extends Roo.bootstrap.Component
30107  * Bootstrap NavProgressItem class
30108  * @cfg {String} rid the reference id
30109  * @cfg {Boolean} active (true|false) Is item active default false
30110  * @cfg {Boolean} disabled (true|false) Is item active default false
30111  * @cfg {String} html
30112  * @cfg {String} position (top|bottom) text position default bottom
30113  * @cfg {String} icon show icon instead of number
30114  * 
30115  * @constructor
30116  * Create a new NavProgressItem
30117  * @param {Object} config The config object
30118  */
30119 Roo.bootstrap.NavProgressItem = function(config){
30120     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30121     this.addEvents({
30122         // raw events
30123         /**
30124          * @event click
30125          * The raw click event for the entire grid.
30126          * @param {Roo.bootstrap.NavProgressItem} this
30127          * @param {Roo.EventObject} e
30128          */
30129         "click" : true
30130     });
30131    
30132 };
30133
30134 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30135     
30136     rid : '',
30137     active : false,
30138     disabled : false,
30139     html : '',
30140     position : 'bottom',
30141     icon : false,
30142     
30143     getAutoCreate : function()
30144     {
30145         var iconCls = 'roo-navigation-bar-item-icon';
30146         
30147         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30148         
30149         var cfg = {
30150             tag: 'li',
30151             cls: 'roo-navigation-bar-item',
30152             cn : [
30153                 {
30154                     tag : 'i',
30155                     cls : iconCls
30156                 }
30157             ]
30158         };
30159         
30160         if(this.active){
30161             cfg.cls += ' active';
30162         }
30163         if(this.disabled){
30164             cfg.cls += ' disabled';
30165         }
30166         
30167         return cfg;
30168     },
30169     
30170     disable : function()
30171     {
30172         this.setDisabled(true);
30173     },
30174     
30175     enable : function()
30176     {
30177         this.setDisabled(false);
30178     },
30179     
30180     initEvents: function() 
30181     {
30182         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30183         
30184         this.iconEl.on('click', this.onClick, this);
30185     },
30186     
30187     onClick : function(e)
30188     {
30189         e.preventDefault();
30190         
30191         if(this.disabled){
30192             return;
30193         }
30194         
30195         if(this.fireEvent('click', this, e) === false){
30196             return;
30197         };
30198         
30199         this.parent().setActiveItem(this);
30200     },
30201     
30202     isActive: function () 
30203     {
30204         return this.active;
30205     },
30206     
30207     setActive : function(state)
30208     {
30209         if(this.active == state){
30210             return;
30211         }
30212         
30213         this.active = state;
30214         
30215         if (state) {
30216             this.el.addClass('active');
30217             return;
30218         }
30219         
30220         this.el.removeClass('active');
30221         
30222         return;
30223     },
30224     
30225     setDisabled : function(state)
30226     {
30227         if(this.disabled == state){
30228             return;
30229         }
30230         
30231         this.disabled = state;
30232         
30233         if (state) {
30234             this.el.addClass('disabled');
30235             return;
30236         }
30237         
30238         this.el.removeClass('disabled');
30239     },
30240     
30241     tooltipEl : function()
30242     {
30243         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30244     }
30245 });
30246  
30247
30248  /*
30249  * - LGPL
30250  *
30251  * FieldLabel
30252  * 
30253  */
30254
30255 /**
30256  * @class Roo.bootstrap.FieldLabel
30257  * @extends Roo.bootstrap.Component
30258  * Bootstrap FieldLabel class
30259  * @cfg {String} html contents of the element
30260  * @cfg {String} tag tag of the element default label
30261  * @cfg {String} cls class of the element
30262  * @cfg {String} target label target 
30263  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30264  * @cfg {String} invalidClass default "text-warning"
30265  * @cfg {String} validClass default "text-success"
30266  * @cfg {String} iconTooltip default "This field is required"
30267  * @cfg {String} indicatorpos (left|right) default left
30268  * 
30269  * @constructor
30270  * Create a new FieldLabel
30271  * @param {Object} config The config object
30272  */
30273
30274 Roo.bootstrap.FieldLabel = function(config){
30275     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30276     
30277     this.addEvents({
30278             /**
30279              * @event invalid
30280              * Fires after the field has been marked as invalid.
30281              * @param {Roo.form.FieldLabel} this
30282              * @param {String} msg The validation message
30283              */
30284             invalid : true,
30285             /**
30286              * @event valid
30287              * Fires after the field has been validated with no errors.
30288              * @param {Roo.form.FieldLabel} this
30289              */
30290             valid : true
30291         });
30292 };
30293
30294 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30295     
30296     tag: 'label',
30297     cls: '',
30298     html: '',
30299     target: '',
30300     allowBlank : true,
30301     invalidClass : 'has-warning',
30302     validClass : 'has-success',
30303     iconTooltip : 'This field is required',
30304     indicatorpos : 'left',
30305     
30306     getAutoCreate : function(){
30307         
30308         var cls = "";
30309         if (!this.allowBlank) {
30310             cls  = "visible";
30311         }
30312         
30313         var cfg = {
30314             tag : this.tag,
30315             cls : 'roo-bootstrap-field-label ' + this.cls,
30316             for : this.target,
30317             cn : [
30318                 {
30319                     tag : 'i',
30320                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30321                     tooltip : this.iconTooltip
30322                 },
30323                 {
30324                     tag : 'span',
30325                     html : this.html
30326                 }
30327             ] 
30328         };
30329         
30330         if(this.indicatorpos == 'right'){
30331             var cfg = {
30332                 tag : this.tag,
30333                 cls : 'roo-bootstrap-field-label ' + this.cls,
30334                 for : this.target,
30335                 cn : [
30336                     {
30337                         tag : 'span',
30338                         html : this.html
30339                     },
30340                     {
30341                         tag : 'i',
30342                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30343                         tooltip : this.iconTooltip
30344                     }
30345                 ] 
30346             };
30347         }
30348         
30349         return cfg;
30350     },
30351     
30352     initEvents: function() 
30353     {
30354         Roo.bootstrap.Element.superclass.initEvents.call(this);
30355         
30356         this.indicator = this.indicatorEl();
30357         
30358         if(this.indicator){
30359             this.indicator.removeClass('visible');
30360             this.indicator.addClass('invisible');
30361         }
30362         
30363         Roo.bootstrap.FieldLabel.register(this);
30364     },
30365     
30366     indicatorEl : function()
30367     {
30368         var indicator = this.el.select('i.roo-required-indicator',true).first();
30369         
30370         if(!indicator){
30371             return false;
30372         }
30373         
30374         return indicator;
30375         
30376     },
30377     
30378     /**
30379      * Mark this field as valid
30380      */
30381     markValid : function()
30382     {
30383         if(this.indicator){
30384             this.indicator.removeClass('visible');
30385             this.indicator.addClass('invisible');
30386         }
30387         
30388         this.el.removeClass(this.invalidClass);
30389         
30390         this.el.addClass(this.validClass);
30391         
30392         this.fireEvent('valid', this);
30393     },
30394     
30395     /**
30396      * Mark this field as invalid
30397      * @param {String} msg The validation message
30398      */
30399     markInvalid : function(msg)
30400     {
30401         if(this.indicator){
30402             this.indicator.removeClass('invisible');
30403             this.indicator.addClass('visible');
30404         }
30405         
30406         this.el.removeClass(this.validClass);
30407         
30408         this.el.addClass(this.invalidClass);
30409         
30410         this.fireEvent('invalid', this, msg);
30411     }
30412     
30413    
30414 });
30415
30416 Roo.apply(Roo.bootstrap.FieldLabel, {
30417     
30418     groups: {},
30419     
30420      /**
30421     * register a FieldLabel Group
30422     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30423     */
30424     register : function(label)
30425     {
30426         if(this.groups.hasOwnProperty(label.target)){
30427             return;
30428         }
30429      
30430         this.groups[label.target] = label;
30431         
30432     },
30433     /**
30434     * fetch a FieldLabel Group based on the target
30435     * @param {string} target
30436     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30437     */
30438     get: function(target) {
30439         if (typeof(this.groups[target]) == 'undefined') {
30440             return false;
30441         }
30442         
30443         return this.groups[target] ;
30444     }
30445 });
30446
30447  
30448
30449  /*
30450  * - LGPL
30451  *
30452  * page DateSplitField.
30453  * 
30454  */
30455
30456
30457 /**
30458  * @class Roo.bootstrap.DateSplitField
30459  * @extends Roo.bootstrap.Component
30460  * Bootstrap DateSplitField class
30461  * @cfg {string} fieldLabel - the label associated
30462  * @cfg {Number} labelWidth set the width of label (0-12)
30463  * @cfg {String} labelAlign (top|left)
30464  * @cfg {Boolean} dayAllowBlank (true|false) default false
30465  * @cfg {Boolean} monthAllowBlank (true|false) default false
30466  * @cfg {Boolean} yearAllowBlank (true|false) default false
30467  * @cfg {string} dayPlaceholder 
30468  * @cfg {string} monthPlaceholder
30469  * @cfg {string} yearPlaceholder
30470  * @cfg {string} dayFormat default 'd'
30471  * @cfg {string} monthFormat default 'm'
30472  * @cfg {string} yearFormat default 'Y'
30473  * @cfg {Number} labellg set the width of label (1-12)
30474  * @cfg {Number} labelmd set the width of label (1-12)
30475  * @cfg {Number} labelsm set the width of label (1-12)
30476  * @cfg {Number} labelxs set the width of label (1-12)
30477
30478  *     
30479  * @constructor
30480  * Create a new DateSplitField
30481  * @param {Object} config The config object
30482  */
30483
30484 Roo.bootstrap.DateSplitField = function(config){
30485     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30486     
30487     this.addEvents({
30488         // raw events
30489          /**
30490          * @event years
30491          * getting the data of years
30492          * @param {Roo.bootstrap.DateSplitField} this
30493          * @param {Object} years
30494          */
30495         "years" : true,
30496         /**
30497          * @event days
30498          * getting the data of days
30499          * @param {Roo.bootstrap.DateSplitField} this
30500          * @param {Object} days
30501          */
30502         "days" : true,
30503         /**
30504          * @event invalid
30505          * Fires after the field has been marked as invalid.
30506          * @param {Roo.form.Field} this
30507          * @param {String} msg The validation message
30508          */
30509         invalid : true,
30510        /**
30511          * @event valid
30512          * Fires after the field has been validated with no errors.
30513          * @param {Roo.form.Field} this
30514          */
30515         valid : true
30516     });
30517 };
30518
30519 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30520     
30521     fieldLabel : '',
30522     labelAlign : 'top',
30523     labelWidth : 3,
30524     dayAllowBlank : false,
30525     monthAllowBlank : false,
30526     yearAllowBlank : false,
30527     dayPlaceholder : '',
30528     monthPlaceholder : '',
30529     yearPlaceholder : '',
30530     dayFormat : 'd',
30531     monthFormat : 'm',
30532     yearFormat : 'Y',
30533     isFormField : true,
30534     labellg : 0,
30535     labelmd : 0,
30536     labelsm : 0,
30537     labelxs : 0,
30538     
30539     getAutoCreate : function()
30540     {
30541         var cfg = {
30542             tag : 'div',
30543             cls : 'row roo-date-split-field-group',
30544             cn : [
30545                 {
30546                     tag : 'input',
30547                     type : 'hidden',
30548                     cls : 'form-hidden-field roo-date-split-field-group-value',
30549                     name : this.name
30550                 }
30551             ]
30552         };
30553         
30554         var labelCls = 'col-md-12';
30555         var contentCls = 'col-md-4';
30556         
30557         if(this.fieldLabel){
30558             
30559             var label = {
30560                 tag : 'div',
30561                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30562                 cn : [
30563                     {
30564                         tag : 'label',
30565                         html : this.fieldLabel
30566                     }
30567                 ]
30568             };
30569             
30570             if(this.labelAlign == 'left'){
30571             
30572                 if(this.labelWidth > 12){
30573                     label.style = "width: " + this.labelWidth + 'px';
30574                 }
30575
30576                 if(this.labelWidth < 13 && this.labelmd == 0){
30577                     this.labelmd = this.labelWidth;
30578                 }
30579
30580                 if(this.labellg > 0){
30581                     labelCls = ' col-lg-' + this.labellg;
30582                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30583                 }
30584
30585                 if(this.labelmd > 0){
30586                     labelCls = ' col-md-' + this.labelmd;
30587                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30588                 }
30589
30590                 if(this.labelsm > 0){
30591                     labelCls = ' col-sm-' + this.labelsm;
30592                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30593                 }
30594
30595                 if(this.labelxs > 0){
30596                     labelCls = ' col-xs-' + this.labelxs;
30597                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30598                 }
30599             }
30600             
30601             label.cls += ' ' + labelCls;
30602             
30603             cfg.cn.push(label);
30604         }
30605         
30606         Roo.each(['day', 'month', 'year'], function(t){
30607             cfg.cn.push({
30608                 tag : 'div',
30609                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30610             });
30611         }, this);
30612         
30613         return cfg;
30614     },
30615     
30616     inputEl: function ()
30617     {
30618         return this.el.select('.roo-date-split-field-group-value', true).first();
30619     },
30620     
30621     onRender : function(ct, position) 
30622     {
30623         var _this = this;
30624         
30625         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30626         
30627         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30628         
30629         this.dayField = new Roo.bootstrap.ComboBox({
30630             allowBlank : this.dayAllowBlank,
30631             alwaysQuery : true,
30632             displayField : 'value',
30633             editable : false,
30634             fieldLabel : '',
30635             forceSelection : true,
30636             mode : 'local',
30637             placeholder : this.dayPlaceholder,
30638             selectOnFocus : true,
30639             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30640             triggerAction : 'all',
30641             typeAhead : true,
30642             valueField : 'value',
30643             store : new Roo.data.SimpleStore({
30644                 data : (function() {    
30645                     var days = [];
30646                     _this.fireEvent('days', _this, days);
30647                     return days;
30648                 })(),
30649                 fields : [ 'value' ]
30650             }),
30651             listeners : {
30652                 select : function (_self, record, index)
30653                 {
30654                     _this.setValue(_this.getValue());
30655                 }
30656             }
30657         });
30658
30659         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30660         
30661         this.monthField = new Roo.bootstrap.MonthField({
30662             after : '<i class=\"fa fa-calendar\"></i>',
30663             allowBlank : this.monthAllowBlank,
30664             placeholder : this.monthPlaceholder,
30665             readOnly : true,
30666             listeners : {
30667                 render : function (_self)
30668                 {
30669                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30670                         e.preventDefault();
30671                         _self.focus();
30672                     });
30673                 },
30674                 select : function (_self, oldvalue, newvalue)
30675                 {
30676                     _this.setValue(_this.getValue());
30677                 }
30678             }
30679         });
30680         
30681         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30682         
30683         this.yearField = new Roo.bootstrap.ComboBox({
30684             allowBlank : this.yearAllowBlank,
30685             alwaysQuery : true,
30686             displayField : 'value',
30687             editable : false,
30688             fieldLabel : '',
30689             forceSelection : true,
30690             mode : 'local',
30691             placeholder : this.yearPlaceholder,
30692             selectOnFocus : true,
30693             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30694             triggerAction : 'all',
30695             typeAhead : true,
30696             valueField : 'value',
30697             store : new Roo.data.SimpleStore({
30698                 data : (function() {
30699                     var years = [];
30700                     _this.fireEvent('years', _this, years);
30701                     return years;
30702                 })(),
30703                 fields : [ 'value' ]
30704             }),
30705             listeners : {
30706                 select : function (_self, record, index)
30707                 {
30708                     _this.setValue(_this.getValue());
30709                 }
30710             }
30711         });
30712
30713         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30714     },
30715     
30716     setValue : function(v, format)
30717     {
30718         this.inputEl.dom.value = v;
30719         
30720         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30721         
30722         var d = Date.parseDate(v, f);
30723         
30724         if(!d){
30725             this.validate();
30726             return;
30727         }
30728         
30729         this.setDay(d.format(this.dayFormat));
30730         this.setMonth(d.format(this.monthFormat));
30731         this.setYear(d.format(this.yearFormat));
30732         
30733         this.validate();
30734         
30735         return;
30736     },
30737     
30738     setDay : function(v)
30739     {
30740         this.dayField.setValue(v);
30741         this.inputEl.dom.value = this.getValue();
30742         this.validate();
30743         return;
30744     },
30745     
30746     setMonth : function(v)
30747     {
30748         this.monthField.setValue(v, true);
30749         this.inputEl.dom.value = this.getValue();
30750         this.validate();
30751         return;
30752     },
30753     
30754     setYear : function(v)
30755     {
30756         this.yearField.setValue(v);
30757         this.inputEl.dom.value = this.getValue();
30758         this.validate();
30759         return;
30760     },
30761     
30762     getDay : function()
30763     {
30764         return this.dayField.getValue();
30765     },
30766     
30767     getMonth : function()
30768     {
30769         return this.monthField.getValue();
30770     },
30771     
30772     getYear : function()
30773     {
30774         return this.yearField.getValue();
30775     },
30776     
30777     getValue : function()
30778     {
30779         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30780         
30781         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30782         
30783         return date;
30784     },
30785     
30786     reset : function()
30787     {
30788         this.setDay('');
30789         this.setMonth('');
30790         this.setYear('');
30791         this.inputEl.dom.value = '';
30792         this.validate();
30793         return;
30794     },
30795     
30796     validate : function()
30797     {
30798         var d = this.dayField.validate();
30799         var m = this.monthField.validate();
30800         var y = this.yearField.validate();
30801         
30802         var valid = true;
30803         
30804         if(
30805                 (!this.dayAllowBlank && !d) ||
30806                 (!this.monthAllowBlank && !m) ||
30807                 (!this.yearAllowBlank && !y)
30808         ){
30809             valid = false;
30810         }
30811         
30812         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30813             return valid;
30814         }
30815         
30816         if(valid){
30817             this.markValid();
30818             return valid;
30819         }
30820         
30821         this.markInvalid();
30822         
30823         return valid;
30824     },
30825     
30826     markValid : function()
30827     {
30828         
30829         var label = this.el.select('label', true).first();
30830         var icon = this.el.select('i.fa-star', true).first();
30831
30832         if(label && icon){
30833             icon.remove();
30834         }
30835         
30836         this.fireEvent('valid', this);
30837     },
30838     
30839      /**
30840      * Mark this field as invalid
30841      * @param {String} msg The validation message
30842      */
30843     markInvalid : function(msg)
30844     {
30845         
30846         var label = this.el.select('label', true).first();
30847         var icon = this.el.select('i.fa-star', true).first();
30848
30849         if(label && !icon){
30850             this.el.select('.roo-date-split-field-label', true).createChild({
30851                 tag : 'i',
30852                 cls : 'text-danger fa fa-lg fa-star',
30853                 tooltip : 'This field is required',
30854                 style : 'margin-right:5px;'
30855             }, label, true);
30856         }
30857         
30858         this.fireEvent('invalid', this, msg);
30859     },
30860     
30861     clearInvalid : function()
30862     {
30863         var label = this.el.select('label', true).first();
30864         var icon = this.el.select('i.fa-star', true).first();
30865
30866         if(label && icon){
30867             icon.remove();
30868         }
30869         
30870         this.fireEvent('valid', this);
30871     },
30872     
30873     getName: function()
30874     {
30875         return this.name;
30876     }
30877     
30878 });
30879
30880  /**
30881  *
30882  * This is based on 
30883  * http://masonry.desandro.com
30884  *
30885  * The idea is to render all the bricks based on vertical width...
30886  *
30887  * The original code extends 'outlayer' - we might need to use that....
30888  * 
30889  */
30890
30891
30892 /**
30893  * @class Roo.bootstrap.LayoutMasonry
30894  * @extends Roo.bootstrap.Component
30895  * Bootstrap Layout Masonry class
30896  * 
30897  * @constructor
30898  * Create a new Element
30899  * @param {Object} config The config object
30900  */
30901
30902 Roo.bootstrap.LayoutMasonry = function(config){
30903     
30904     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30905     
30906     this.bricks = [];
30907     
30908     Roo.bootstrap.LayoutMasonry.register(this);
30909     
30910     this.addEvents({
30911         // raw events
30912         /**
30913          * @event layout
30914          * Fire after layout the items
30915          * @param {Roo.bootstrap.LayoutMasonry} this
30916          * @param {Roo.EventObject} e
30917          */
30918         "layout" : true
30919     });
30920     
30921 };
30922
30923 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30924     
30925     /**
30926      * @cfg {Boolean} isLayoutInstant = no animation?
30927      */   
30928     isLayoutInstant : false, // needed?
30929    
30930     /**
30931      * @cfg {Number} boxWidth  width of the columns
30932      */   
30933     boxWidth : 450,
30934     
30935       /**
30936      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30937      */   
30938     boxHeight : 0,
30939     
30940     /**
30941      * @cfg {Number} padWidth padding below box..
30942      */   
30943     padWidth : 10, 
30944     
30945     /**
30946      * @cfg {Number} gutter gutter width..
30947      */   
30948     gutter : 10,
30949     
30950      /**
30951      * @cfg {Number} maxCols maximum number of columns
30952      */   
30953     
30954     maxCols: 0,
30955     
30956     /**
30957      * @cfg {Boolean} isAutoInitial defalut true
30958      */   
30959     isAutoInitial : true, 
30960     
30961     containerWidth: 0,
30962     
30963     /**
30964      * @cfg {Boolean} isHorizontal defalut false
30965      */   
30966     isHorizontal : false, 
30967
30968     currentSize : null,
30969     
30970     tag: 'div',
30971     
30972     cls: '',
30973     
30974     bricks: null, //CompositeElement
30975     
30976     cols : 1,
30977     
30978     _isLayoutInited : false,
30979     
30980 //    isAlternative : false, // only use for vertical layout...
30981     
30982     /**
30983      * @cfg {Number} alternativePadWidth padding below box..
30984      */   
30985     alternativePadWidth : 50,
30986     
30987     selectedBrick : [],
30988     
30989     getAutoCreate : function(){
30990         
30991         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30992         
30993         var cfg = {
30994             tag: this.tag,
30995             cls: 'blog-masonary-wrapper ' + this.cls,
30996             cn : {
30997                 cls : 'mas-boxes masonary'
30998             }
30999         };
31000         
31001         return cfg;
31002     },
31003     
31004     getChildContainer: function( )
31005     {
31006         if (this.boxesEl) {
31007             return this.boxesEl;
31008         }
31009         
31010         this.boxesEl = this.el.select('.mas-boxes').first();
31011         
31012         return this.boxesEl;
31013     },
31014     
31015     
31016     initEvents : function()
31017     {
31018         var _this = this;
31019         
31020         if(this.isAutoInitial){
31021             Roo.log('hook children rendered');
31022             this.on('childrenrendered', function() {
31023                 Roo.log('children rendered');
31024                 _this.initial();
31025             } ,this);
31026         }
31027     },
31028     
31029     initial : function()
31030     {
31031         this.selectedBrick = [];
31032         
31033         this.currentSize = this.el.getBox(true);
31034         
31035         Roo.EventManager.onWindowResize(this.resize, this); 
31036
31037         if(!this.isAutoInitial){
31038             this.layout();
31039             return;
31040         }
31041         
31042         this.layout();
31043         
31044         return;
31045         //this.layout.defer(500,this);
31046         
31047     },
31048     
31049     resize : function()
31050     {
31051         var cs = this.el.getBox(true);
31052         
31053         if (
31054                 this.currentSize.width == cs.width && 
31055                 this.currentSize.x == cs.x && 
31056                 this.currentSize.height == cs.height && 
31057                 this.currentSize.y == cs.y 
31058         ) {
31059             Roo.log("no change in with or X or Y");
31060             return;
31061         }
31062         
31063         this.currentSize = cs;
31064         
31065         this.layout();
31066         
31067     },
31068     
31069     layout : function()
31070     {   
31071         this._resetLayout();
31072         
31073         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31074         
31075         this.layoutItems( isInstant );
31076       
31077         this._isLayoutInited = true;
31078         
31079         this.fireEvent('layout', this);
31080         
31081     },
31082     
31083     _resetLayout : function()
31084     {
31085         if(this.isHorizontal){
31086             this.horizontalMeasureColumns();
31087             return;
31088         }
31089         
31090         this.verticalMeasureColumns();
31091         
31092     },
31093     
31094     verticalMeasureColumns : function()
31095     {
31096         this.getContainerWidth();
31097         
31098 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31099 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31100 //            return;
31101 //        }
31102         
31103         var boxWidth = this.boxWidth + this.padWidth;
31104         
31105         if(this.containerWidth < this.boxWidth){
31106             boxWidth = this.containerWidth
31107         }
31108         
31109         var containerWidth = this.containerWidth;
31110         
31111         var cols = Math.floor(containerWidth / boxWidth);
31112         
31113         this.cols = Math.max( cols, 1 );
31114         
31115         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31116         
31117         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31118         
31119         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31120         
31121         this.colWidth = boxWidth + avail - this.padWidth;
31122         
31123         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31124         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31125     },
31126     
31127     horizontalMeasureColumns : function()
31128     {
31129         this.getContainerWidth();
31130         
31131         var boxWidth = this.boxWidth;
31132         
31133         if(this.containerWidth < boxWidth){
31134             boxWidth = this.containerWidth;
31135         }
31136         
31137         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31138         
31139         this.el.setHeight(boxWidth);
31140         
31141     },
31142     
31143     getContainerWidth : function()
31144     {
31145         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31146     },
31147     
31148     layoutItems : function( isInstant )
31149     {
31150         Roo.log(this.bricks);
31151         
31152         var items = Roo.apply([], this.bricks);
31153         
31154         if(this.isHorizontal){
31155             this._horizontalLayoutItems( items , isInstant );
31156             return;
31157         }
31158         
31159 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31160 //            this._verticalAlternativeLayoutItems( items , isInstant );
31161 //            return;
31162 //        }
31163         
31164         this._verticalLayoutItems( items , isInstant );
31165         
31166     },
31167     
31168     _verticalLayoutItems : function ( items , isInstant)
31169     {
31170         if ( !items || !items.length ) {
31171             return;
31172         }
31173         
31174         var standard = [
31175             ['xs', 'xs', 'xs', 'tall'],
31176             ['xs', 'xs', 'tall'],
31177             ['xs', 'xs', 'sm'],
31178             ['xs', 'xs', 'xs'],
31179             ['xs', 'tall'],
31180             ['xs', 'sm'],
31181             ['xs', 'xs'],
31182             ['xs'],
31183             
31184             ['sm', 'xs', 'xs'],
31185             ['sm', 'xs'],
31186             ['sm'],
31187             
31188             ['tall', 'xs', 'xs', 'xs'],
31189             ['tall', 'xs', 'xs'],
31190             ['tall', 'xs'],
31191             ['tall']
31192             
31193         ];
31194         
31195         var queue = [];
31196         
31197         var boxes = [];
31198         
31199         var box = [];
31200         
31201         Roo.each(items, function(item, k){
31202             
31203             switch (item.size) {
31204                 // these layouts take up a full box,
31205                 case 'md' :
31206                 case 'md-left' :
31207                 case 'md-right' :
31208                 case 'wide' :
31209                     
31210                     if(box.length){
31211                         boxes.push(box);
31212                         box = [];
31213                     }
31214                     
31215                     boxes.push([item]);
31216                     
31217                     break;
31218                     
31219                 case 'xs' :
31220                 case 'sm' :
31221                 case 'tall' :
31222                     
31223                     box.push(item);
31224                     
31225                     break;
31226                 default :
31227                     break;
31228                     
31229             }
31230             
31231         }, this);
31232         
31233         if(box.length){
31234             boxes.push(box);
31235             box = [];
31236         }
31237         
31238         var filterPattern = function(box, length)
31239         {
31240             if(!box.length){
31241                 return;
31242             }
31243             
31244             var match = false;
31245             
31246             var pattern = box.slice(0, length);
31247             
31248             var format = [];
31249             
31250             Roo.each(pattern, function(i){
31251                 format.push(i.size);
31252             }, this);
31253             
31254             Roo.each(standard, function(s){
31255                 
31256                 if(String(s) != String(format)){
31257                     return;
31258                 }
31259                 
31260                 match = true;
31261                 return false;
31262                 
31263             }, this);
31264             
31265             if(!match && length == 1){
31266                 return;
31267             }
31268             
31269             if(!match){
31270                 filterPattern(box, length - 1);
31271                 return;
31272             }
31273                 
31274             queue.push(pattern);
31275
31276             box = box.slice(length, box.length);
31277
31278             filterPattern(box, 4);
31279
31280             return;
31281             
31282         }
31283         
31284         Roo.each(boxes, function(box, k){
31285             
31286             if(!box.length){
31287                 return;
31288             }
31289             
31290             if(box.length == 1){
31291                 queue.push(box);
31292                 return;
31293             }
31294             
31295             filterPattern(box, 4);
31296             
31297         }, this);
31298         
31299         this._processVerticalLayoutQueue( queue, isInstant );
31300         
31301     },
31302     
31303 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31304 //    {
31305 //        if ( !items || !items.length ) {
31306 //            return;
31307 //        }
31308 //
31309 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31310 //        
31311 //    },
31312     
31313     _horizontalLayoutItems : function ( items , isInstant)
31314     {
31315         if ( !items || !items.length || items.length < 3) {
31316             return;
31317         }
31318         
31319         items.reverse();
31320         
31321         var eItems = items.slice(0, 3);
31322         
31323         items = items.slice(3, items.length);
31324         
31325         var standard = [
31326             ['xs', 'xs', 'xs', 'wide'],
31327             ['xs', 'xs', 'wide'],
31328             ['xs', 'xs', 'sm'],
31329             ['xs', 'xs', 'xs'],
31330             ['xs', 'wide'],
31331             ['xs', 'sm'],
31332             ['xs', 'xs'],
31333             ['xs'],
31334             
31335             ['sm', 'xs', 'xs'],
31336             ['sm', 'xs'],
31337             ['sm'],
31338             
31339             ['wide', 'xs', 'xs', 'xs'],
31340             ['wide', 'xs', 'xs'],
31341             ['wide', 'xs'],
31342             ['wide'],
31343             
31344             ['wide-thin']
31345         ];
31346         
31347         var queue = [];
31348         
31349         var boxes = [];
31350         
31351         var box = [];
31352         
31353         Roo.each(items, function(item, k){
31354             
31355             switch (item.size) {
31356                 case 'md' :
31357                 case 'md-left' :
31358                 case 'md-right' :
31359                 case 'tall' :
31360                     
31361                     if(box.length){
31362                         boxes.push(box);
31363                         box = [];
31364                     }
31365                     
31366                     boxes.push([item]);
31367                     
31368                     break;
31369                     
31370                 case 'xs' :
31371                 case 'sm' :
31372                 case 'wide' :
31373                 case 'wide-thin' :
31374                     
31375                     box.push(item);
31376                     
31377                     break;
31378                 default :
31379                     break;
31380                     
31381             }
31382             
31383         }, this);
31384         
31385         if(box.length){
31386             boxes.push(box);
31387             box = [];
31388         }
31389         
31390         var filterPattern = function(box, length)
31391         {
31392             if(!box.length){
31393                 return;
31394             }
31395             
31396             var match = false;
31397             
31398             var pattern = box.slice(0, length);
31399             
31400             var format = [];
31401             
31402             Roo.each(pattern, function(i){
31403                 format.push(i.size);
31404             }, this);
31405             
31406             Roo.each(standard, function(s){
31407                 
31408                 if(String(s) != String(format)){
31409                     return;
31410                 }
31411                 
31412                 match = true;
31413                 return false;
31414                 
31415             }, this);
31416             
31417             if(!match && length == 1){
31418                 return;
31419             }
31420             
31421             if(!match){
31422                 filterPattern(box, length - 1);
31423                 return;
31424             }
31425                 
31426             queue.push(pattern);
31427
31428             box = box.slice(length, box.length);
31429
31430             filterPattern(box, 4);
31431
31432             return;
31433             
31434         }
31435         
31436         Roo.each(boxes, function(box, k){
31437             
31438             if(!box.length){
31439                 return;
31440             }
31441             
31442             if(box.length == 1){
31443                 queue.push(box);
31444                 return;
31445             }
31446             
31447             filterPattern(box, 4);
31448             
31449         }, this);
31450         
31451         
31452         var prune = [];
31453         
31454         var pos = this.el.getBox(true);
31455         
31456         var minX = pos.x;
31457         
31458         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31459         
31460         var hit_end = false;
31461         
31462         Roo.each(queue, function(box){
31463             
31464             if(hit_end){
31465                 
31466                 Roo.each(box, function(b){
31467                 
31468                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31469                     b.el.hide();
31470
31471                 }, this);
31472
31473                 return;
31474             }
31475             
31476             var mx = 0;
31477             
31478             Roo.each(box, function(b){
31479                 
31480                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31481                 b.el.show();
31482
31483                 mx = Math.max(mx, b.x);
31484                 
31485             }, this);
31486             
31487             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31488             
31489             if(maxX < minX){
31490                 
31491                 Roo.each(box, function(b){
31492                 
31493                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31494                     b.el.hide();
31495                     
31496                 }, this);
31497                 
31498                 hit_end = true;
31499                 
31500                 return;
31501             }
31502             
31503             prune.push(box);
31504             
31505         }, this);
31506         
31507         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31508     },
31509     
31510     /** Sets position of item in DOM
31511     * @param {Element} item
31512     * @param {Number} x - horizontal position
31513     * @param {Number} y - vertical position
31514     * @param {Boolean} isInstant - disables transitions
31515     */
31516     _processVerticalLayoutQueue : function( queue, isInstant )
31517     {
31518         var pos = this.el.getBox(true);
31519         var x = pos.x;
31520         var y = pos.y;
31521         var maxY = [];
31522         
31523         for (var i = 0; i < this.cols; i++){
31524             maxY[i] = pos.y;
31525         }
31526         
31527         Roo.each(queue, function(box, k){
31528             
31529             var col = k % this.cols;
31530             
31531             Roo.each(box, function(b,kk){
31532                 
31533                 b.el.position('absolute');
31534                 
31535                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31536                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31537                 
31538                 if(b.size == 'md-left' || b.size == 'md-right'){
31539                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31540                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31541                 }
31542                 
31543                 b.el.setWidth(width);
31544                 b.el.setHeight(height);
31545                 // iframe?
31546                 b.el.select('iframe',true).setSize(width,height);
31547                 
31548             }, this);
31549             
31550             for (var i = 0; i < this.cols; i++){
31551                 
31552                 if(maxY[i] < maxY[col]){
31553                     col = i;
31554                     continue;
31555                 }
31556                 
31557                 col = Math.min(col, i);
31558                 
31559             }
31560             
31561             x = pos.x + col * (this.colWidth + this.padWidth);
31562             
31563             y = maxY[col];
31564             
31565             var positions = [];
31566             
31567             switch (box.length){
31568                 case 1 :
31569                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31570                     break;
31571                 case 2 :
31572                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31573                     break;
31574                 case 3 :
31575                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31576                     break;
31577                 case 4 :
31578                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31579                     break;
31580                 default :
31581                     break;
31582             }
31583             
31584             Roo.each(box, function(b,kk){
31585                 
31586                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31587                 
31588                 var sz = b.el.getSize();
31589                 
31590                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31591                 
31592             }, this);
31593             
31594         }, this);
31595         
31596         var mY = 0;
31597         
31598         for (var i = 0; i < this.cols; i++){
31599             mY = Math.max(mY, maxY[i]);
31600         }
31601         
31602         this.el.setHeight(mY - pos.y);
31603         
31604     },
31605     
31606 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31607 //    {
31608 //        var pos = this.el.getBox(true);
31609 //        var x = pos.x;
31610 //        var y = pos.y;
31611 //        var maxX = pos.right;
31612 //        
31613 //        var maxHeight = 0;
31614 //        
31615 //        Roo.each(items, function(item, k){
31616 //            
31617 //            var c = k % 2;
31618 //            
31619 //            item.el.position('absolute');
31620 //                
31621 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31622 //
31623 //            item.el.setWidth(width);
31624 //
31625 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31626 //
31627 //            item.el.setHeight(height);
31628 //            
31629 //            if(c == 0){
31630 //                item.el.setXY([x, y], isInstant ? false : true);
31631 //            } else {
31632 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31633 //            }
31634 //            
31635 //            y = y + height + this.alternativePadWidth;
31636 //            
31637 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31638 //            
31639 //        }, this);
31640 //        
31641 //        this.el.setHeight(maxHeight);
31642 //        
31643 //    },
31644     
31645     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31646     {
31647         var pos = this.el.getBox(true);
31648         
31649         var minX = pos.x;
31650         var minY = pos.y;
31651         
31652         var maxX = pos.right;
31653         
31654         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31655         
31656         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31657         
31658         Roo.each(queue, function(box, k){
31659             
31660             Roo.each(box, function(b, kk){
31661                 
31662                 b.el.position('absolute');
31663                 
31664                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31665                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31666                 
31667                 if(b.size == 'md-left' || b.size == 'md-right'){
31668                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31669                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31670                 }
31671                 
31672                 b.el.setWidth(width);
31673                 b.el.setHeight(height);
31674                 
31675             }, this);
31676             
31677             if(!box.length){
31678                 return;
31679             }
31680             
31681             var positions = [];
31682             
31683             switch (box.length){
31684                 case 1 :
31685                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31686                     break;
31687                 case 2 :
31688                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31689                     break;
31690                 case 3 :
31691                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31692                     break;
31693                 case 4 :
31694                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31695                     break;
31696                 default :
31697                     break;
31698             }
31699             
31700             Roo.each(box, function(b,kk){
31701                 
31702                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31703                 
31704                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31705                 
31706             }, this);
31707             
31708         }, this);
31709         
31710     },
31711     
31712     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31713     {
31714         Roo.each(eItems, function(b,k){
31715             
31716             b.size = (k == 0) ? 'sm' : 'xs';
31717             b.x = (k == 0) ? 2 : 1;
31718             b.y = (k == 0) ? 2 : 1;
31719             
31720             b.el.position('absolute');
31721             
31722             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31723                 
31724             b.el.setWidth(width);
31725             
31726             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31727             
31728             b.el.setHeight(height);
31729             
31730         }, this);
31731
31732         var positions = [];
31733         
31734         positions.push({
31735             x : maxX - this.unitWidth * 2 - this.gutter,
31736             y : minY
31737         });
31738         
31739         positions.push({
31740             x : maxX - this.unitWidth,
31741             y : minY + (this.unitWidth + this.gutter) * 2
31742         });
31743         
31744         positions.push({
31745             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31746             y : minY
31747         });
31748         
31749         Roo.each(eItems, function(b,k){
31750             
31751             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31752
31753         }, this);
31754         
31755     },
31756     
31757     getVerticalOneBoxColPositions : function(x, y, box)
31758     {
31759         var pos = [];
31760         
31761         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31762         
31763         if(box[0].size == 'md-left'){
31764             rand = 0;
31765         }
31766         
31767         if(box[0].size == 'md-right'){
31768             rand = 1;
31769         }
31770         
31771         pos.push({
31772             x : x + (this.unitWidth + this.gutter) * rand,
31773             y : y
31774         });
31775         
31776         return pos;
31777     },
31778     
31779     getVerticalTwoBoxColPositions : function(x, y, box)
31780     {
31781         var pos = [];
31782         
31783         if(box[0].size == 'xs'){
31784             
31785             pos.push({
31786                 x : x,
31787                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31788             });
31789
31790             pos.push({
31791                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31792                 y : y
31793             });
31794             
31795             return pos;
31796             
31797         }
31798         
31799         pos.push({
31800             x : x,
31801             y : y
31802         });
31803
31804         pos.push({
31805             x : x + (this.unitWidth + this.gutter) * 2,
31806             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31807         });
31808         
31809         return pos;
31810         
31811     },
31812     
31813     getVerticalThreeBoxColPositions : function(x, y, box)
31814     {
31815         var pos = [];
31816         
31817         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31818             
31819             pos.push({
31820                 x : x,
31821                 y : y
31822             });
31823
31824             pos.push({
31825                 x : x + (this.unitWidth + this.gutter) * 1,
31826                 y : y
31827             });
31828             
31829             pos.push({
31830                 x : x + (this.unitWidth + this.gutter) * 2,
31831                 y : y
31832             });
31833             
31834             return pos;
31835             
31836         }
31837         
31838         if(box[0].size == 'xs' && box[1].size == 'xs'){
31839             
31840             pos.push({
31841                 x : x,
31842                 y : y
31843             });
31844
31845             pos.push({
31846                 x : x,
31847                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31848             });
31849             
31850             pos.push({
31851                 x : x + (this.unitWidth + this.gutter) * 1,
31852                 y : y
31853             });
31854             
31855             return pos;
31856             
31857         }
31858         
31859         pos.push({
31860             x : x,
31861             y : y
31862         });
31863
31864         pos.push({
31865             x : x + (this.unitWidth + this.gutter) * 2,
31866             y : y
31867         });
31868
31869         pos.push({
31870             x : x + (this.unitWidth + this.gutter) * 2,
31871             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31872         });
31873             
31874         return pos;
31875         
31876     },
31877     
31878     getVerticalFourBoxColPositions : function(x, y, box)
31879     {
31880         var pos = [];
31881         
31882         if(box[0].size == 'xs'){
31883             
31884             pos.push({
31885                 x : x,
31886                 y : y
31887             });
31888
31889             pos.push({
31890                 x : x,
31891                 y : y + (this.unitHeight + this.gutter) * 1
31892             });
31893             
31894             pos.push({
31895                 x : x,
31896                 y : y + (this.unitHeight + this.gutter) * 2
31897             });
31898             
31899             pos.push({
31900                 x : x + (this.unitWidth + this.gutter) * 1,
31901                 y : y
31902             });
31903             
31904             return pos;
31905             
31906         }
31907         
31908         pos.push({
31909             x : x,
31910             y : y
31911         });
31912
31913         pos.push({
31914             x : x + (this.unitWidth + this.gutter) * 2,
31915             y : y
31916         });
31917
31918         pos.push({
31919             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31920             y : y + (this.unitHeight + this.gutter) * 1
31921         });
31922
31923         pos.push({
31924             x : x + (this.unitWidth + this.gutter) * 2,
31925             y : y + (this.unitWidth + this.gutter) * 2
31926         });
31927
31928         return pos;
31929         
31930     },
31931     
31932     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31933     {
31934         var pos = [];
31935         
31936         if(box[0].size == 'md-left'){
31937             pos.push({
31938                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31939                 y : minY
31940             });
31941             
31942             return pos;
31943         }
31944         
31945         if(box[0].size == 'md-right'){
31946             pos.push({
31947                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31948                 y : minY + (this.unitWidth + this.gutter) * 1
31949             });
31950             
31951             return pos;
31952         }
31953         
31954         var rand = Math.floor(Math.random() * (4 - box[0].y));
31955         
31956         pos.push({
31957             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31958             y : minY + (this.unitWidth + this.gutter) * rand
31959         });
31960         
31961         return pos;
31962         
31963     },
31964     
31965     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31966     {
31967         var pos = [];
31968         
31969         if(box[0].size == 'xs'){
31970             
31971             pos.push({
31972                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31973                 y : minY
31974             });
31975
31976             pos.push({
31977                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31978                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31979             });
31980             
31981             return pos;
31982             
31983         }
31984         
31985         pos.push({
31986             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31987             y : minY
31988         });
31989
31990         pos.push({
31991             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31992             y : minY + (this.unitWidth + this.gutter) * 2
31993         });
31994         
31995         return pos;
31996         
31997     },
31998     
31999     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32000     {
32001         var pos = [];
32002         
32003         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32004             
32005             pos.push({
32006                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32007                 y : minY
32008             });
32009
32010             pos.push({
32011                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32012                 y : minY + (this.unitWidth + this.gutter) * 1
32013             });
32014             
32015             pos.push({
32016                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32017                 y : minY + (this.unitWidth + this.gutter) * 2
32018             });
32019             
32020             return pos;
32021             
32022         }
32023         
32024         if(box[0].size == 'xs' && box[1].size == 'xs'){
32025             
32026             pos.push({
32027                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32028                 y : minY
32029             });
32030
32031             pos.push({
32032                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32033                 y : minY
32034             });
32035             
32036             pos.push({
32037                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32038                 y : minY + (this.unitWidth + this.gutter) * 1
32039             });
32040             
32041             return pos;
32042             
32043         }
32044         
32045         pos.push({
32046             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32047             y : minY
32048         });
32049
32050         pos.push({
32051             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32052             y : minY + (this.unitWidth + this.gutter) * 2
32053         });
32054
32055         pos.push({
32056             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32057             y : minY + (this.unitWidth + this.gutter) * 2
32058         });
32059             
32060         return pos;
32061         
32062     },
32063     
32064     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32065     {
32066         var pos = [];
32067         
32068         if(box[0].size == 'xs'){
32069             
32070             pos.push({
32071                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32072                 y : minY
32073             });
32074
32075             pos.push({
32076                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32077                 y : minY
32078             });
32079             
32080             pos.push({
32081                 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),
32082                 y : minY
32083             });
32084             
32085             pos.push({
32086                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32087                 y : minY + (this.unitWidth + this.gutter) * 1
32088             });
32089             
32090             return pos;
32091             
32092         }
32093         
32094         pos.push({
32095             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32096             y : minY
32097         });
32098         
32099         pos.push({
32100             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32101             y : minY + (this.unitWidth + this.gutter) * 2
32102         });
32103         
32104         pos.push({
32105             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32106             y : minY + (this.unitWidth + this.gutter) * 2
32107         });
32108         
32109         pos.push({
32110             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),
32111             y : minY + (this.unitWidth + this.gutter) * 2
32112         });
32113
32114         return pos;
32115         
32116     },
32117     
32118     /**
32119     * remove a Masonry Brick
32120     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32121     */
32122     removeBrick : function(brick_id)
32123     {
32124         if (!brick_id) {
32125             return;
32126         }
32127         
32128         for (var i = 0; i<this.bricks.length; i++) {
32129             if (this.bricks[i].id == brick_id) {
32130                 this.bricks.splice(i,1);
32131                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32132                 this.initial();
32133             }
32134         }
32135     },
32136     
32137     /**
32138     * adds a Masonry Brick
32139     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32140     */
32141     addBrick : function(cfg)
32142     {
32143         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32144         //this.register(cn);
32145         cn.parentId = this.id;
32146         cn.render(this.el);
32147         return cn;
32148     },
32149     
32150     /**
32151     * register a Masonry Brick
32152     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32153     */
32154     
32155     register : function(brick)
32156     {
32157         this.bricks.push(brick);
32158         brick.masonryId = this.id;
32159     },
32160     
32161     /**
32162     * clear all the Masonry Brick
32163     */
32164     clearAll : function()
32165     {
32166         this.bricks = [];
32167         //this.getChildContainer().dom.innerHTML = "";
32168         this.el.dom.innerHTML = '';
32169     },
32170     
32171     getSelected : function()
32172     {
32173         if (!this.selectedBrick) {
32174             return false;
32175         }
32176         
32177         return this.selectedBrick;
32178     }
32179 });
32180
32181 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32182     
32183     groups: {},
32184      /**
32185     * register a Masonry Layout
32186     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32187     */
32188     
32189     register : function(layout)
32190     {
32191         this.groups[layout.id] = layout;
32192     },
32193     /**
32194     * fetch a  Masonry Layout based on the masonry layout ID
32195     * @param {string} the masonry layout to add
32196     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32197     */
32198     
32199     get: function(layout_id) {
32200         if (typeof(this.groups[layout_id]) == 'undefined') {
32201             return false;
32202         }
32203         return this.groups[layout_id] ;
32204     }
32205     
32206     
32207     
32208 });
32209
32210  
32211
32212  /**
32213  *
32214  * This is based on 
32215  * http://masonry.desandro.com
32216  *
32217  * The idea is to render all the bricks based on vertical width...
32218  *
32219  * The original code extends 'outlayer' - we might need to use that....
32220  * 
32221  */
32222
32223
32224 /**
32225  * @class Roo.bootstrap.LayoutMasonryAuto
32226  * @extends Roo.bootstrap.Component
32227  * Bootstrap Layout Masonry class
32228  * 
32229  * @constructor
32230  * Create a new Element
32231  * @param {Object} config The config object
32232  */
32233
32234 Roo.bootstrap.LayoutMasonryAuto = function(config){
32235     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32236 };
32237
32238 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32239     
32240       /**
32241      * @cfg {Boolean} isFitWidth  - resize the width..
32242      */   
32243     isFitWidth : false,  // options..
32244     /**
32245      * @cfg {Boolean} isOriginLeft = left align?
32246      */   
32247     isOriginLeft : true,
32248     /**
32249      * @cfg {Boolean} isOriginTop = top align?
32250      */   
32251     isOriginTop : false,
32252     /**
32253      * @cfg {Boolean} isLayoutInstant = no animation?
32254      */   
32255     isLayoutInstant : false, // needed?
32256     /**
32257      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32258      */   
32259     isResizingContainer : true,
32260     /**
32261      * @cfg {Number} columnWidth  width of the columns 
32262      */   
32263     
32264     columnWidth : 0,
32265     
32266     /**
32267      * @cfg {Number} maxCols maximum number of columns
32268      */   
32269     
32270     maxCols: 0,
32271     /**
32272      * @cfg {Number} padHeight padding below box..
32273      */   
32274     
32275     padHeight : 10, 
32276     
32277     /**
32278      * @cfg {Boolean} isAutoInitial defalut true
32279      */   
32280     
32281     isAutoInitial : true, 
32282     
32283     // private?
32284     gutter : 0,
32285     
32286     containerWidth: 0,
32287     initialColumnWidth : 0,
32288     currentSize : null,
32289     
32290     colYs : null, // array.
32291     maxY : 0,
32292     padWidth: 10,
32293     
32294     
32295     tag: 'div',
32296     cls: '',
32297     bricks: null, //CompositeElement
32298     cols : 0, // array?
32299     // element : null, // wrapped now this.el
32300     _isLayoutInited : null, 
32301     
32302     
32303     getAutoCreate : function(){
32304         
32305         var cfg = {
32306             tag: this.tag,
32307             cls: 'blog-masonary-wrapper ' + this.cls,
32308             cn : {
32309                 cls : 'mas-boxes masonary'
32310             }
32311         };
32312         
32313         return cfg;
32314     },
32315     
32316     getChildContainer: function( )
32317     {
32318         if (this.boxesEl) {
32319             return this.boxesEl;
32320         }
32321         
32322         this.boxesEl = this.el.select('.mas-boxes').first();
32323         
32324         return this.boxesEl;
32325     },
32326     
32327     
32328     initEvents : function()
32329     {
32330         var _this = this;
32331         
32332         if(this.isAutoInitial){
32333             Roo.log('hook children rendered');
32334             this.on('childrenrendered', function() {
32335                 Roo.log('children rendered');
32336                 _this.initial();
32337             } ,this);
32338         }
32339         
32340     },
32341     
32342     initial : function()
32343     {
32344         this.reloadItems();
32345
32346         this.currentSize = this.el.getBox(true);
32347
32348         /// was window resize... - let's see if this works..
32349         Roo.EventManager.onWindowResize(this.resize, this); 
32350
32351         if(!this.isAutoInitial){
32352             this.layout();
32353             return;
32354         }
32355         
32356         this.layout.defer(500,this);
32357     },
32358     
32359     reloadItems: function()
32360     {
32361         this.bricks = this.el.select('.masonry-brick', true);
32362         
32363         this.bricks.each(function(b) {
32364             //Roo.log(b.getSize());
32365             if (!b.attr('originalwidth')) {
32366                 b.attr('originalwidth',  b.getSize().width);
32367             }
32368             
32369         });
32370         
32371         Roo.log(this.bricks.elements.length);
32372     },
32373     
32374     resize : function()
32375     {
32376         Roo.log('resize');
32377         var cs = this.el.getBox(true);
32378         
32379         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32380             Roo.log("no change in with or X");
32381             return;
32382         }
32383         this.currentSize = cs;
32384         this.layout();
32385     },
32386     
32387     layout : function()
32388     {
32389          Roo.log('layout');
32390         this._resetLayout();
32391         //this._manageStamps();
32392       
32393         // don't animate first layout
32394         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32395         this.layoutItems( isInstant );
32396       
32397         // flag for initalized
32398         this._isLayoutInited = true;
32399     },
32400     
32401     layoutItems : function( isInstant )
32402     {
32403         //var items = this._getItemsForLayout( this.items );
32404         // original code supports filtering layout items.. we just ignore it..
32405         
32406         this._layoutItems( this.bricks , isInstant );
32407       
32408         this._postLayout();
32409     },
32410     _layoutItems : function ( items , isInstant)
32411     {
32412        //this.fireEvent( 'layout', this, items );
32413     
32414
32415         if ( !items || !items.elements.length ) {
32416           // no items, emit event with empty array
32417             return;
32418         }
32419
32420         var queue = [];
32421         items.each(function(item) {
32422             Roo.log("layout item");
32423             Roo.log(item);
32424             // get x/y object from method
32425             var position = this._getItemLayoutPosition( item );
32426             // enqueue
32427             position.item = item;
32428             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32429             queue.push( position );
32430         }, this);
32431       
32432         this._processLayoutQueue( queue );
32433     },
32434     /** Sets position of item in DOM
32435     * @param {Element} item
32436     * @param {Number} x - horizontal position
32437     * @param {Number} y - vertical position
32438     * @param {Boolean} isInstant - disables transitions
32439     */
32440     _processLayoutQueue : function( queue )
32441     {
32442         for ( var i=0, len = queue.length; i < len; i++ ) {
32443             var obj = queue[i];
32444             obj.item.position('absolute');
32445             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32446         }
32447     },
32448       
32449     
32450     /**
32451     * Any logic you want to do after each layout,
32452     * i.e. size the container
32453     */
32454     _postLayout : function()
32455     {
32456         this.resizeContainer();
32457     },
32458     
32459     resizeContainer : function()
32460     {
32461         if ( !this.isResizingContainer ) {
32462             return;
32463         }
32464         var size = this._getContainerSize();
32465         if ( size ) {
32466             this.el.setSize(size.width,size.height);
32467             this.boxesEl.setSize(size.width,size.height);
32468         }
32469     },
32470     
32471     
32472     
32473     _resetLayout : function()
32474     {
32475         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32476         this.colWidth = this.el.getWidth();
32477         //this.gutter = this.el.getWidth(); 
32478         
32479         this.measureColumns();
32480
32481         // reset column Y
32482         var i = this.cols;
32483         this.colYs = [];
32484         while (i--) {
32485             this.colYs.push( 0 );
32486         }
32487     
32488         this.maxY = 0;
32489     },
32490
32491     measureColumns : function()
32492     {
32493         this.getContainerWidth();
32494       // if columnWidth is 0, default to outerWidth of first item
32495         if ( !this.columnWidth ) {
32496             var firstItem = this.bricks.first();
32497             Roo.log(firstItem);
32498             this.columnWidth  = this.containerWidth;
32499             if (firstItem && firstItem.attr('originalwidth') ) {
32500                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32501             }
32502             // columnWidth fall back to item of first element
32503             Roo.log("set column width?");
32504                         this.initialColumnWidth = this.columnWidth  ;
32505
32506             // if first elem has no width, default to size of container
32507             
32508         }
32509         
32510         
32511         if (this.initialColumnWidth) {
32512             this.columnWidth = this.initialColumnWidth;
32513         }
32514         
32515         
32516             
32517         // column width is fixed at the top - however if container width get's smaller we should
32518         // reduce it...
32519         
32520         // this bit calcs how man columns..
32521             
32522         var columnWidth = this.columnWidth += this.gutter;
32523       
32524         // calculate columns
32525         var containerWidth = this.containerWidth + this.gutter;
32526         
32527         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32528         // fix rounding errors, typically with gutters
32529         var excess = columnWidth - containerWidth % columnWidth;
32530         
32531         
32532         // if overshoot is less than a pixel, round up, otherwise floor it
32533         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32534         cols = Math[ mathMethod ]( cols );
32535         this.cols = Math.max( cols, 1 );
32536         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32537         
32538          // padding positioning..
32539         var totalColWidth = this.cols * this.columnWidth;
32540         var padavail = this.containerWidth - totalColWidth;
32541         // so for 2 columns - we need 3 'pads'
32542         
32543         var padNeeded = (1+this.cols) * this.padWidth;
32544         
32545         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32546         
32547         this.columnWidth += padExtra
32548         //this.padWidth = Math.floor(padavail /  ( this.cols));
32549         
32550         // adjust colum width so that padding is fixed??
32551         
32552         // we have 3 columns ... total = width * 3
32553         // we have X left over... that should be used by 
32554         
32555         //if (this.expandC) {
32556             
32557         //}
32558         
32559         
32560         
32561     },
32562     
32563     getContainerWidth : function()
32564     {
32565        /* // container is parent if fit width
32566         var container = this.isFitWidth ? this.element.parentNode : this.element;
32567         // check that this.size and size are there
32568         // IE8 triggers resize on body size change, so they might not be
32569         
32570         var size = getSize( container );  //FIXME
32571         this.containerWidth = size && size.innerWidth; //FIXME
32572         */
32573          
32574         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32575         
32576     },
32577     
32578     _getItemLayoutPosition : function( item )  // what is item?
32579     {
32580         // we resize the item to our columnWidth..
32581       
32582         item.setWidth(this.columnWidth);
32583         item.autoBoxAdjust  = false;
32584         
32585         var sz = item.getSize();
32586  
32587         // how many columns does this brick span
32588         var remainder = this.containerWidth % this.columnWidth;
32589         
32590         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32591         // round if off by 1 pixel, otherwise use ceil
32592         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32593         colSpan = Math.min( colSpan, this.cols );
32594         
32595         // normally this should be '1' as we dont' currently allow multi width columns..
32596         
32597         var colGroup = this._getColGroup( colSpan );
32598         // get the minimum Y value from the columns
32599         var minimumY = Math.min.apply( Math, colGroup );
32600         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32601         
32602         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32603          
32604         // position the brick
32605         var position = {
32606             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32607             y: this.currentSize.y + minimumY + this.padHeight
32608         };
32609         
32610         Roo.log(position);
32611         // apply setHeight to necessary columns
32612         var setHeight = minimumY + sz.height + this.padHeight;
32613         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32614         
32615         var setSpan = this.cols + 1 - colGroup.length;
32616         for ( var i = 0; i < setSpan; i++ ) {
32617           this.colYs[ shortColIndex + i ] = setHeight ;
32618         }
32619       
32620         return position;
32621     },
32622     
32623     /**
32624      * @param {Number} colSpan - number of columns the element spans
32625      * @returns {Array} colGroup
32626      */
32627     _getColGroup : function( colSpan )
32628     {
32629         if ( colSpan < 2 ) {
32630           // if brick spans only one column, use all the column Ys
32631           return this.colYs;
32632         }
32633       
32634         var colGroup = [];
32635         // how many different places could this brick fit horizontally
32636         var groupCount = this.cols + 1 - colSpan;
32637         // for each group potential horizontal position
32638         for ( var i = 0; i < groupCount; i++ ) {
32639           // make an array of colY values for that one group
32640           var groupColYs = this.colYs.slice( i, i + colSpan );
32641           // and get the max value of the array
32642           colGroup[i] = Math.max.apply( Math, groupColYs );
32643         }
32644         return colGroup;
32645     },
32646     /*
32647     _manageStamp : function( stamp )
32648     {
32649         var stampSize =  stamp.getSize();
32650         var offset = stamp.getBox();
32651         // get the columns that this stamp affects
32652         var firstX = this.isOriginLeft ? offset.x : offset.right;
32653         var lastX = firstX + stampSize.width;
32654         var firstCol = Math.floor( firstX / this.columnWidth );
32655         firstCol = Math.max( 0, firstCol );
32656         
32657         var lastCol = Math.floor( lastX / this.columnWidth );
32658         // lastCol should not go over if multiple of columnWidth #425
32659         lastCol -= lastX % this.columnWidth ? 0 : 1;
32660         lastCol = Math.min( this.cols - 1, lastCol );
32661         
32662         // set colYs to bottom of the stamp
32663         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32664             stampSize.height;
32665             
32666         for ( var i = firstCol; i <= lastCol; i++ ) {
32667           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32668         }
32669     },
32670     */
32671     
32672     _getContainerSize : function()
32673     {
32674         this.maxY = Math.max.apply( Math, this.colYs );
32675         var size = {
32676             height: this.maxY
32677         };
32678       
32679         if ( this.isFitWidth ) {
32680             size.width = this._getContainerFitWidth();
32681         }
32682       
32683         return size;
32684     },
32685     
32686     _getContainerFitWidth : function()
32687     {
32688         var unusedCols = 0;
32689         // count unused columns
32690         var i = this.cols;
32691         while ( --i ) {
32692           if ( this.colYs[i] !== 0 ) {
32693             break;
32694           }
32695           unusedCols++;
32696         }
32697         // fit container to columns that have been used
32698         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32699     },
32700     
32701     needsResizeLayout : function()
32702     {
32703         var previousWidth = this.containerWidth;
32704         this.getContainerWidth();
32705         return previousWidth !== this.containerWidth;
32706     }
32707  
32708 });
32709
32710  
32711
32712  /*
32713  * - LGPL
32714  *
32715  * element
32716  * 
32717  */
32718
32719 /**
32720  * @class Roo.bootstrap.MasonryBrick
32721  * @extends Roo.bootstrap.Component
32722  * Bootstrap MasonryBrick class
32723  * 
32724  * @constructor
32725  * Create a new MasonryBrick
32726  * @param {Object} config The config object
32727  */
32728
32729 Roo.bootstrap.MasonryBrick = function(config){
32730     
32731     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32732     
32733     Roo.bootstrap.MasonryBrick.register(this);
32734     
32735     this.addEvents({
32736         // raw events
32737         /**
32738          * @event click
32739          * When a MasonryBrick is clcik
32740          * @param {Roo.bootstrap.MasonryBrick} this
32741          * @param {Roo.EventObject} e
32742          */
32743         "click" : true
32744     });
32745 };
32746
32747 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32748     
32749     /**
32750      * @cfg {String} title
32751      */   
32752     title : '',
32753     /**
32754      * @cfg {String} html
32755      */   
32756     html : '',
32757     /**
32758      * @cfg {String} bgimage
32759      */   
32760     bgimage : '',
32761     /**
32762      * @cfg {String} videourl
32763      */   
32764     videourl : '',
32765     /**
32766      * @cfg {String} cls
32767      */   
32768     cls : '',
32769     /**
32770      * @cfg {String} href
32771      */   
32772     href : '',
32773     /**
32774      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32775      */   
32776     size : 'xs',
32777     
32778     /**
32779      * @cfg {String} placetitle (center|bottom)
32780      */   
32781     placetitle : '',
32782     
32783     /**
32784      * @cfg {Boolean} isFitContainer defalut true
32785      */   
32786     isFitContainer : true, 
32787     
32788     /**
32789      * @cfg {Boolean} preventDefault defalut false
32790      */   
32791     preventDefault : false, 
32792     
32793     /**
32794      * @cfg {Boolean} inverse defalut false
32795      */   
32796     maskInverse : false, 
32797     
32798     getAutoCreate : function()
32799     {
32800         if(!this.isFitContainer){
32801             return this.getSplitAutoCreate();
32802         }
32803         
32804         var cls = 'masonry-brick masonry-brick-full';
32805         
32806         if(this.href.length){
32807             cls += ' masonry-brick-link';
32808         }
32809         
32810         if(this.bgimage.length){
32811             cls += ' masonry-brick-image';
32812         }
32813         
32814         if(this.maskInverse){
32815             cls += ' mask-inverse';
32816         }
32817         
32818         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32819             cls += ' enable-mask';
32820         }
32821         
32822         if(this.size){
32823             cls += ' masonry-' + this.size + '-brick';
32824         }
32825         
32826         if(this.placetitle.length){
32827             
32828             switch (this.placetitle) {
32829                 case 'center' :
32830                     cls += ' masonry-center-title';
32831                     break;
32832                 case 'bottom' :
32833                     cls += ' masonry-bottom-title';
32834                     break;
32835                 default:
32836                     break;
32837             }
32838             
32839         } else {
32840             if(!this.html.length && !this.bgimage.length){
32841                 cls += ' masonry-center-title';
32842             }
32843
32844             if(!this.html.length && this.bgimage.length){
32845                 cls += ' masonry-bottom-title';
32846             }
32847         }
32848         
32849         if(this.cls){
32850             cls += ' ' + this.cls;
32851         }
32852         
32853         var cfg = {
32854             tag: (this.href.length) ? 'a' : 'div',
32855             cls: cls,
32856             cn: [
32857                 {
32858                     tag: 'div',
32859                     cls: 'masonry-brick-mask'
32860                 },
32861                 {
32862                     tag: 'div',
32863                     cls: 'masonry-brick-paragraph',
32864                     cn: []
32865                 }
32866             ]
32867         };
32868         
32869         if(this.href.length){
32870             cfg.href = this.href;
32871         }
32872         
32873         var cn = cfg.cn[1].cn;
32874         
32875         if(this.title.length){
32876             cn.push({
32877                 tag: 'h4',
32878                 cls: 'masonry-brick-title',
32879                 html: this.title
32880             });
32881         }
32882         
32883         if(this.html.length){
32884             cn.push({
32885                 tag: 'p',
32886                 cls: 'masonry-brick-text',
32887                 html: this.html
32888             });
32889         }
32890         
32891         if (!this.title.length && !this.html.length) {
32892             cfg.cn[1].cls += ' hide';
32893         }
32894         
32895         if(this.bgimage.length){
32896             cfg.cn.push({
32897                 tag: 'img',
32898                 cls: 'masonry-brick-image-view',
32899                 src: this.bgimage
32900             });
32901         }
32902         
32903         if(this.videourl.length){
32904             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32905             // youtube support only?
32906             cfg.cn.push({
32907                 tag: 'iframe',
32908                 cls: 'masonry-brick-image-view',
32909                 src: vurl,
32910                 frameborder : 0,
32911                 allowfullscreen : true
32912             });
32913         }
32914         
32915         return cfg;
32916         
32917     },
32918     
32919     getSplitAutoCreate : function()
32920     {
32921         var cls = 'masonry-brick masonry-brick-split';
32922         
32923         if(this.href.length){
32924             cls += ' masonry-brick-link';
32925         }
32926         
32927         if(this.bgimage.length){
32928             cls += ' masonry-brick-image';
32929         }
32930         
32931         if(this.size){
32932             cls += ' masonry-' + this.size + '-brick';
32933         }
32934         
32935         switch (this.placetitle) {
32936             case 'center' :
32937                 cls += ' masonry-center-title';
32938                 break;
32939             case 'bottom' :
32940                 cls += ' masonry-bottom-title';
32941                 break;
32942             default:
32943                 if(!this.bgimage.length){
32944                     cls += ' masonry-center-title';
32945                 }
32946
32947                 if(this.bgimage.length){
32948                     cls += ' masonry-bottom-title';
32949                 }
32950                 break;
32951         }
32952         
32953         if(this.cls){
32954             cls += ' ' + this.cls;
32955         }
32956         
32957         var cfg = {
32958             tag: (this.href.length) ? 'a' : 'div',
32959             cls: cls,
32960             cn: [
32961                 {
32962                     tag: 'div',
32963                     cls: 'masonry-brick-split-head',
32964                     cn: [
32965                         {
32966                             tag: 'div',
32967                             cls: 'masonry-brick-paragraph',
32968                             cn: []
32969                         }
32970                     ]
32971                 },
32972                 {
32973                     tag: 'div',
32974                     cls: 'masonry-brick-split-body',
32975                     cn: []
32976                 }
32977             ]
32978         };
32979         
32980         if(this.href.length){
32981             cfg.href = this.href;
32982         }
32983         
32984         if(this.title.length){
32985             cfg.cn[0].cn[0].cn.push({
32986                 tag: 'h4',
32987                 cls: 'masonry-brick-title',
32988                 html: this.title
32989             });
32990         }
32991         
32992         if(this.html.length){
32993             cfg.cn[1].cn.push({
32994                 tag: 'p',
32995                 cls: 'masonry-brick-text',
32996                 html: this.html
32997             });
32998         }
32999
33000         if(this.bgimage.length){
33001             cfg.cn[0].cn.push({
33002                 tag: 'img',
33003                 cls: 'masonry-brick-image-view',
33004                 src: this.bgimage
33005             });
33006         }
33007         
33008         if(this.videourl.length){
33009             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33010             // youtube support only?
33011             cfg.cn[0].cn.cn.push({
33012                 tag: 'iframe',
33013                 cls: 'masonry-brick-image-view',
33014                 src: vurl,
33015                 frameborder : 0,
33016                 allowfullscreen : true
33017             });
33018         }
33019         
33020         return cfg;
33021     },
33022     
33023     initEvents: function() 
33024     {
33025         switch (this.size) {
33026             case 'xs' :
33027                 this.x = 1;
33028                 this.y = 1;
33029                 break;
33030             case 'sm' :
33031                 this.x = 2;
33032                 this.y = 2;
33033                 break;
33034             case 'md' :
33035             case 'md-left' :
33036             case 'md-right' :
33037                 this.x = 3;
33038                 this.y = 3;
33039                 break;
33040             case 'tall' :
33041                 this.x = 2;
33042                 this.y = 3;
33043                 break;
33044             case 'wide' :
33045                 this.x = 3;
33046                 this.y = 2;
33047                 break;
33048             case 'wide-thin' :
33049                 this.x = 3;
33050                 this.y = 1;
33051                 break;
33052                         
33053             default :
33054                 break;
33055         }
33056         
33057         if(Roo.isTouch){
33058             this.el.on('touchstart', this.onTouchStart, this);
33059             this.el.on('touchmove', this.onTouchMove, this);
33060             this.el.on('touchend', this.onTouchEnd, this);
33061             this.el.on('contextmenu', this.onContextMenu, this);
33062         } else {
33063             this.el.on('mouseenter'  ,this.enter, this);
33064             this.el.on('mouseleave', this.leave, this);
33065             this.el.on('click', this.onClick, this);
33066         }
33067         
33068         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33069             this.parent().bricks.push(this);   
33070         }
33071         
33072     },
33073     
33074     onClick: function(e, el)
33075     {
33076         var time = this.endTimer - this.startTimer;
33077         // Roo.log(e.preventDefault());
33078         if(Roo.isTouch){
33079             if(time > 1000){
33080                 e.preventDefault();
33081                 return;
33082             }
33083         }
33084         
33085         if(!this.preventDefault){
33086             return;
33087         }
33088         
33089         e.preventDefault();
33090         
33091         if (this.activeClass != '') {
33092             this.selectBrick();
33093         }
33094         
33095         this.fireEvent('click', this, e);
33096     },
33097     
33098     enter: function(e, el)
33099     {
33100         e.preventDefault();
33101         
33102         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33103             return;
33104         }
33105         
33106         if(this.bgimage.length && this.html.length){
33107             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33108         }
33109     },
33110     
33111     leave: function(e, el)
33112     {
33113         e.preventDefault();
33114         
33115         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33116             return;
33117         }
33118         
33119         if(this.bgimage.length && this.html.length){
33120             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33121         }
33122     },
33123     
33124     onTouchStart: function(e, el)
33125     {
33126 //        e.preventDefault();
33127         
33128         this.touchmoved = false;
33129         
33130         if(!this.isFitContainer){
33131             return;
33132         }
33133         
33134         if(!this.bgimage.length || !this.html.length){
33135             return;
33136         }
33137         
33138         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33139         
33140         this.timer = new Date().getTime();
33141         
33142     },
33143     
33144     onTouchMove: function(e, el)
33145     {
33146         this.touchmoved = true;
33147     },
33148     
33149     onContextMenu : function(e,el)
33150     {
33151         e.preventDefault();
33152         e.stopPropagation();
33153         return false;
33154     },
33155     
33156     onTouchEnd: function(e, el)
33157     {
33158 //        e.preventDefault();
33159         
33160         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33161         
33162             this.leave(e,el);
33163             
33164             return;
33165         }
33166         
33167         if(!this.bgimage.length || !this.html.length){
33168             
33169             if(this.href.length){
33170                 window.location.href = this.href;
33171             }
33172             
33173             return;
33174         }
33175         
33176         if(!this.isFitContainer){
33177             return;
33178         }
33179         
33180         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33181         
33182         window.location.href = this.href;
33183     },
33184     
33185     //selection on single brick only
33186     selectBrick : function() {
33187         
33188         if (!this.parentId) {
33189             return;
33190         }
33191         
33192         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33193         var index = m.selectedBrick.indexOf(this.id);
33194         
33195         if ( index > -1) {
33196             m.selectedBrick.splice(index,1);
33197             this.el.removeClass(this.activeClass);
33198             return;
33199         }
33200         
33201         for(var i = 0; i < m.selectedBrick.length; i++) {
33202             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33203             b.el.removeClass(b.activeClass);
33204         }
33205         
33206         m.selectedBrick = [];
33207         
33208         m.selectedBrick.push(this.id);
33209         this.el.addClass(this.activeClass);
33210         return;
33211     },
33212     
33213     isSelected : function(){
33214         return this.el.hasClass(this.activeClass);
33215         
33216     }
33217 });
33218
33219 Roo.apply(Roo.bootstrap.MasonryBrick, {
33220     
33221     //groups: {},
33222     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33223      /**
33224     * register a Masonry Brick
33225     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33226     */
33227     
33228     register : function(brick)
33229     {
33230         //this.groups[brick.id] = brick;
33231         this.groups.add(brick.id, brick);
33232     },
33233     /**
33234     * fetch a  masonry brick based on the masonry brick ID
33235     * @param {string} the masonry brick to add
33236     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33237     */
33238     
33239     get: function(brick_id) 
33240     {
33241         // if (typeof(this.groups[brick_id]) == 'undefined') {
33242         //     return false;
33243         // }
33244         // return this.groups[brick_id] ;
33245         
33246         if(this.groups.key(brick_id)) {
33247             return this.groups.key(brick_id);
33248         }
33249         
33250         return false;
33251     }
33252     
33253     
33254     
33255 });
33256
33257  /*
33258  * - LGPL
33259  *
33260  * element
33261  * 
33262  */
33263
33264 /**
33265  * @class Roo.bootstrap.Brick
33266  * @extends Roo.bootstrap.Component
33267  * Bootstrap Brick class
33268  * 
33269  * @constructor
33270  * Create a new Brick
33271  * @param {Object} config The config object
33272  */
33273
33274 Roo.bootstrap.Brick = function(config){
33275     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33276     
33277     this.addEvents({
33278         // raw events
33279         /**
33280          * @event click
33281          * When a Brick is click
33282          * @param {Roo.bootstrap.Brick} this
33283          * @param {Roo.EventObject} e
33284          */
33285         "click" : true
33286     });
33287 };
33288
33289 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33290     
33291     /**
33292      * @cfg {String} title
33293      */   
33294     title : '',
33295     /**
33296      * @cfg {String} html
33297      */   
33298     html : '',
33299     /**
33300      * @cfg {String} bgimage
33301      */   
33302     bgimage : '',
33303     /**
33304      * @cfg {String} cls
33305      */   
33306     cls : '',
33307     /**
33308      * @cfg {String} href
33309      */   
33310     href : '',
33311     /**
33312      * @cfg {String} video
33313      */   
33314     video : '',
33315     /**
33316      * @cfg {Boolean} square
33317      */   
33318     square : true,
33319     
33320     getAutoCreate : function()
33321     {
33322         var cls = 'roo-brick';
33323         
33324         if(this.href.length){
33325             cls += ' roo-brick-link';
33326         }
33327         
33328         if(this.bgimage.length){
33329             cls += ' roo-brick-image';
33330         }
33331         
33332         if(!this.html.length && !this.bgimage.length){
33333             cls += ' roo-brick-center-title';
33334         }
33335         
33336         if(!this.html.length && this.bgimage.length){
33337             cls += ' roo-brick-bottom-title';
33338         }
33339         
33340         if(this.cls){
33341             cls += ' ' + this.cls;
33342         }
33343         
33344         var cfg = {
33345             tag: (this.href.length) ? 'a' : 'div',
33346             cls: cls,
33347             cn: [
33348                 {
33349                     tag: 'div',
33350                     cls: 'roo-brick-paragraph',
33351                     cn: []
33352                 }
33353             ]
33354         };
33355         
33356         if(this.href.length){
33357             cfg.href = this.href;
33358         }
33359         
33360         var cn = cfg.cn[0].cn;
33361         
33362         if(this.title.length){
33363             cn.push({
33364                 tag: 'h4',
33365                 cls: 'roo-brick-title',
33366                 html: this.title
33367             });
33368         }
33369         
33370         if(this.html.length){
33371             cn.push({
33372                 tag: 'p',
33373                 cls: 'roo-brick-text',
33374                 html: this.html
33375             });
33376         } else {
33377             cn.cls += ' hide';
33378         }
33379         
33380         if(this.bgimage.length){
33381             cfg.cn.push({
33382                 tag: 'img',
33383                 cls: 'roo-brick-image-view',
33384                 src: this.bgimage
33385             });
33386         }
33387         
33388         return cfg;
33389     },
33390     
33391     initEvents: function() 
33392     {
33393         if(this.title.length || this.html.length){
33394             this.el.on('mouseenter'  ,this.enter, this);
33395             this.el.on('mouseleave', this.leave, this);
33396         }
33397         
33398         Roo.EventManager.onWindowResize(this.resize, this); 
33399         
33400         if(this.bgimage.length){
33401             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33402             this.imageEl.on('load', this.onImageLoad, this);
33403             return;
33404         }
33405         
33406         this.resize();
33407     },
33408     
33409     onImageLoad : function()
33410     {
33411         this.resize();
33412     },
33413     
33414     resize : function()
33415     {
33416         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33417         
33418         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33419         
33420         if(this.bgimage.length){
33421             var image = this.el.select('.roo-brick-image-view', true).first();
33422             
33423             image.setWidth(paragraph.getWidth());
33424             
33425             if(this.square){
33426                 image.setHeight(paragraph.getWidth());
33427             }
33428             
33429             this.el.setHeight(image.getHeight());
33430             paragraph.setHeight(image.getHeight());
33431             
33432         }
33433         
33434     },
33435     
33436     enter: function(e, el)
33437     {
33438         e.preventDefault();
33439         
33440         if(this.bgimage.length){
33441             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33442             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33443         }
33444     },
33445     
33446     leave: function(e, el)
33447     {
33448         e.preventDefault();
33449         
33450         if(this.bgimage.length){
33451             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33452             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33453         }
33454     }
33455     
33456 });
33457
33458  
33459
33460  /*
33461  * - LGPL
33462  *
33463  * Number field 
33464  */
33465
33466 /**
33467  * @class Roo.bootstrap.NumberField
33468  * @extends Roo.bootstrap.Input
33469  * Bootstrap NumberField class
33470  * 
33471  * 
33472  * 
33473  * 
33474  * @constructor
33475  * Create a new NumberField
33476  * @param {Object} config The config object
33477  */
33478
33479 Roo.bootstrap.NumberField = function(config){
33480     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33481 };
33482
33483 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33484     
33485     /**
33486      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33487      */
33488     allowDecimals : true,
33489     /**
33490      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33491      */
33492     decimalSeparator : ".",
33493     /**
33494      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33495      */
33496     decimalPrecision : 2,
33497     /**
33498      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33499      */
33500     allowNegative : true,
33501     
33502     /**
33503      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33504      */
33505     allowZero: true,
33506     /**
33507      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33508      */
33509     minValue : Number.NEGATIVE_INFINITY,
33510     /**
33511      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33512      */
33513     maxValue : Number.MAX_VALUE,
33514     /**
33515      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33516      */
33517     minText : "The minimum value for this field is {0}",
33518     /**
33519      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33520      */
33521     maxText : "The maximum value for this field is {0}",
33522     /**
33523      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33524      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33525      */
33526     nanText : "{0} is not a valid number",
33527     /**
33528      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33529      */
33530     thousandsDelimiter : false,
33531     /**
33532      * @cfg {String} valueAlign alignment of value
33533      */
33534     valueAlign : "left",
33535
33536     getAutoCreate : function()
33537     {
33538         var hiddenInput = {
33539             tag: 'input',
33540             type: 'hidden',
33541             id: Roo.id(),
33542             cls: 'hidden-number-input'
33543         };
33544         
33545         if (this.name) {
33546             hiddenInput.name = this.name;
33547         }
33548         
33549         this.name = '';
33550         
33551         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33552         
33553         this.name = hiddenInput.name;
33554         
33555         if(cfg.cn.length > 0) {
33556             cfg.cn.push(hiddenInput);
33557         }
33558         
33559         return cfg;
33560     },
33561
33562     // private
33563     initEvents : function()
33564     {   
33565         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33566         
33567         var allowed = "0123456789";
33568         
33569         if(this.allowDecimals){
33570             allowed += this.decimalSeparator;
33571         }
33572         
33573         if(this.allowNegative){
33574             allowed += "-";
33575         }
33576         
33577         if(this.thousandsDelimiter) {
33578             allowed += ",";
33579         }
33580         
33581         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33582         
33583         var keyPress = function(e){
33584             
33585             var k = e.getKey();
33586             
33587             var c = e.getCharCode();
33588             
33589             if(
33590                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33591                     allowed.indexOf(String.fromCharCode(c)) === -1
33592             ){
33593                 e.stopEvent();
33594                 return;
33595             }
33596             
33597             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33598                 return;
33599             }
33600             
33601             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33602                 e.stopEvent();
33603             }
33604         };
33605         
33606         this.el.on("keypress", keyPress, this);
33607     },
33608     
33609     validateValue : function(value)
33610     {
33611         
33612         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33613             return false;
33614         }
33615         
33616         var num = this.parseValue(value);
33617         
33618         if(isNaN(num)){
33619             this.markInvalid(String.format(this.nanText, value));
33620             return false;
33621         }
33622         
33623         if(num < this.minValue){
33624             this.markInvalid(String.format(this.minText, this.minValue));
33625             return false;
33626         }
33627         
33628         if(num > this.maxValue){
33629             this.markInvalid(String.format(this.maxText, this.maxValue));
33630             return false;
33631         }
33632         
33633         return true;
33634     },
33635
33636     getValue : function()
33637     {
33638         var v = this.hiddenEl().getValue();
33639         
33640         return this.fixPrecision(this.parseValue(v));
33641     },
33642
33643     parseValue : function(value)
33644     {
33645         if(this.thousandsDelimiter) {
33646             value += "";
33647             r = new RegExp(",", "g");
33648             value = value.replace(r, "");
33649         }
33650         
33651         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33652         return isNaN(value) ? '' : value;
33653     },
33654
33655     fixPrecision : function(value)
33656     {
33657         if(this.thousandsDelimiter) {
33658             value += "";
33659             r = new RegExp(",", "g");
33660             value = value.replace(r, "");
33661         }
33662         
33663         var nan = isNaN(value);
33664         
33665         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33666             return nan ? '' : value;
33667         }
33668         return parseFloat(value).toFixed(this.decimalPrecision);
33669     },
33670
33671     setValue : function(v)
33672     {
33673         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33674         
33675         this.value = v;
33676         
33677         if(this.rendered){
33678             
33679             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33680             
33681             this.inputEl().dom.value = (v == '') ? '' :
33682                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33683             
33684             if(!this.allowZero && v === '0') {
33685                 this.hiddenEl().dom.value = '';
33686                 this.inputEl().dom.value = '';
33687             }
33688             
33689             this.validate();
33690         }
33691     },
33692
33693     decimalPrecisionFcn : function(v)
33694     {
33695         return Math.floor(v);
33696     },
33697
33698     beforeBlur : function()
33699     {
33700         var v = this.parseValue(this.getRawValue());
33701         
33702         if(v || v === 0 || v === ''){
33703             this.setValue(v);
33704         }
33705     },
33706     
33707     hiddenEl : function()
33708     {
33709         return this.el.select('input.hidden-number-input',true).first();
33710     }
33711     
33712 });
33713
33714  
33715
33716 /*
33717 * Licence: LGPL
33718 */
33719
33720 /**
33721  * @class Roo.bootstrap.DocumentSlider
33722  * @extends Roo.bootstrap.Component
33723  * Bootstrap DocumentSlider class
33724  * 
33725  * @constructor
33726  * Create a new DocumentViewer
33727  * @param {Object} config The config object
33728  */
33729
33730 Roo.bootstrap.DocumentSlider = function(config){
33731     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33732     
33733     this.files = [];
33734     
33735     this.addEvents({
33736         /**
33737          * @event initial
33738          * Fire after initEvent
33739          * @param {Roo.bootstrap.DocumentSlider} this
33740          */
33741         "initial" : true,
33742         /**
33743          * @event update
33744          * Fire after update
33745          * @param {Roo.bootstrap.DocumentSlider} this
33746          */
33747         "update" : true,
33748         /**
33749          * @event click
33750          * Fire after click
33751          * @param {Roo.bootstrap.DocumentSlider} this
33752          */
33753         "click" : true
33754     });
33755 };
33756
33757 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33758     
33759     files : false,
33760     
33761     indicator : 0,
33762     
33763     getAutoCreate : function()
33764     {
33765         var cfg = {
33766             tag : 'div',
33767             cls : 'roo-document-slider',
33768             cn : [
33769                 {
33770                     tag : 'div',
33771                     cls : 'roo-document-slider-header',
33772                     cn : [
33773                         {
33774                             tag : 'div',
33775                             cls : 'roo-document-slider-header-title'
33776                         }
33777                     ]
33778                 },
33779                 {
33780                     tag : 'div',
33781                     cls : 'roo-document-slider-body',
33782                     cn : [
33783                         {
33784                             tag : 'div',
33785                             cls : 'roo-document-slider-prev',
33786                             cn : [
33787                                 {
33788                                     tag : 'i',
33789                                     cls : 'fa fa-chevron-left'
33790                                 }
33791                             ]
33792                         },
33793                         {
33794                             tag : 'div',
33795                             cls : 'roo-document-slider-thumb',
33796                             cn : [
33797                                 {
33798                                     tag : 'img',
33799                                     cls : 'roo-document-slider-image'
33800                                 }
33801                             ]
33802                         },
33803                         {
33804                             tag : 'div',
33805                             cls : 'roo-document-slider-next',
33806                             cn : [
33807                                 {
33808                                     tag : 'i',
33809                                     cls : 'fa fa-chevron-right'
33810                                 }
33811                             ]
33812                         }
33813                     ]
33814                 }
33815             ]
33816         };
33817         
33818         return cfg;
33819     },
33820     
33821     initEvents : function()
33822     {
33823         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33824         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33825         
33826         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33827         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33828         
33829         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33830         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33831         
33832         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33833         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33834         
33835         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33836         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33837         
33838         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33839         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33840         
33841         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33842         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33843         
33844         this.thumbEl.on('click', this.onClick, this);
33845         
33846         this.prevIndicator.on('click', this.prev, this);
33847         
33848         this.nextIndicator.on('click', this.next, this);
33849         
33850     },
33851     
33852     initial : function()
33853     {
33854         if(this.files.length){
33855             this.indicator = 1;
33856             this.update()
33857         }
33858         
33859         this.fireEvent('initial', this);
33860     },
33861     
33862     update : function()
33863     {
33864         this.imageEl.attr('src', this.files[this.indicator - 1]);
33865         
33866         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33867         
33868         this.prevIndicator.show();
33869         
33870         if(this.indicator == 1){
33871             this.prevIndicator.hide();
33872         }
33873         
33874         this.nextIndicator.show();
33875         
33876         if(this.indicator == this.files.length){
33877             this.nextIndicator.hide();
33878         }
33879         
33880         this.thumbEl.scrollTo('top');
33881         
33882         this.fireEvent('update', this);
33883     },
33884     
33885     onClick : function(e)
33886     {
33887         e.preventDefault();
33888         
33889         this.fireEvent('click', this);
33890     },
33891     
33892     prev : function(e)
33893     {
33894         e.preventDefault();
33895         
33896         this.indicator = Math.max(1, this.indicator - 1);
33897         
33898         this.update();
33899     },
33900     
33901     next : function(e)
33902     {
33903         e.preventDefault();
33904         
33905         this.indicator = Math.min(this.files.length, this.indicator + 1);
33906         
33907         this.update();
33908     }
33909 });
33910 /*
33911  * - LGPL
33912  *
33913  * RadioSet
33914  *
33915  *
33916  */
33917
33918 /**
33919  * @class Roo.bootstrap.RadioSet
33920  * @extends Roo.bootstrap.Input
33921  * Bootstrap RadioSet class
33922  * @cfg {String} indicatorpos (left|right) default left
33923  * @cfg {Boolean} inline (true|false) inline the element (default true)
33924  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33925  * @constructor
33926  * Create a new RadioSet
33927  * @param {Object} config The config object
33928  */
33929
33930 Roo.bootstrap.RadioSet = function(config){
33931     
33932     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33933     
33934     this.radioes = [];
33935     
33936     Roo.bootstrap.RadioSet.register(this);
33937     
33938     this.addEvents({
33939         /**
33940         * @event check
33941         * Fires when the element is checked or unchecked.
33942         * @param {Roo.bootstrap.RadioSet} this This radio
33943         * @param {Roo.bootstrap.Radio} item The checked item
33944         */
33945        check : true,
33946        /**
33947         * @event click
33948         * Fires when the element is click.
33949         * @param {Roo.bootstrap.RadioSet} this This radio set
33950         * @param {Roo.bootstrap.Radio} item The checked item
33951         * @param {Roo.EventObject} e The event object
33952         */
33953        click : true
33954     });
33955     
33956 };
33957
33958 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33959
33960     radioes : false,
33961     
33962     inline : true,
33963     
33964     weight : '',
33965     
33966     indicatorpos : 'left',
33967     
33968     getAutoCreate : function()
33969     {
33970         var label = {
33971             tag : 'label',
33972             cls : 'roo-radio-set-label',
33973             cn : [
33974                 {
33975                     tag : 'span',
33976                     html : this.fieldLabel
33977                 }
33978             ]
33979         };
33980         
33981         if(this.indicatorpos == 'left'){
33982             label.cn.unshift({
33983                 tag : 'i',
33984                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33985                 tooltip : 'This field is required'
33986             });
33987         } else {
33988             label.cn.push({
33989                 tag : 'i',
33990                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33991                 tooltip : 'This field is required'
33992             });
33993         }
33994         
33995         var items = {
33996             tag : 'div',
33997             cls : 'roo-radio-set-items'
33998         };
33999         
34000         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34001         
34002         if (align === 'left' && this.fieldLabel.length) {
34003             
34004             items = {
34005                 cls : "roo-radio-set-right", 
34006                 cn: [
34007                     items
34008                 ]
34009             };
34010             
34011             if(this.labelWidth > 12){
34012                 label.style = "width: " + this.labelWidth + 'px';
34013             }
34014             
34015             if(this.labelWidth < 13 && this.labelmd == 0){
34016                 this.labelmd = this.labelWidth;
34017             }
34018             
34019             if(this.labellg > 0){
34020                 label.cls += ' col-lg-' + this.labellg;
34021                 items.cls += ' col-lg-' + (12 - this.labellg);
34022             }
34023             
34024             if(this.labelmd > 0){
34025                 label.cls += ' col-md-' + this.labelmd;
34026                 items.cls += ' col-md-' + (12 - this.labelmd);
34027             }
34028             
34029             if(this.labelsm > 0){
34030                 label.cls += ' col-sm-' + this.labelsm;
34031                 items.cls += ' col-sm-' + (12 - this.labelsm);
34032             }
34033             
34034             if(this.labelxs > 0){
34035                 label.cls += ' col-xs-' + this.labelxs;
34036                 items.cls += ' col-xs-' + (12 - this.labelxs);
34037             }
34038         }
34039         
34040         var cfg = {
34041             tag : 'div',
34042             cls : 'roo-radio-set',
34043             cn : [
34044                 {
34045                     tag : 'input',
34046                     cls : 'roo-radio-set-input',
34047                     type : 'hidden',
34048                     name : this.name,
34049                     value : this.value ? this.value :  ''
34050                 },
34051                 label,
34052                 items
34053             ]
34054         };
34055         
34056         if(this.weight.length){
34057             cfg.cls += ' roo-radio-' + this.weight;
34058         }
34059         
34060         if(this.inline) {
34061             cfg.cls += ' roo-radio-set-inline';
34062         }
34063         
34064         var settings=this;
34065         ['xs','sm','md','lg'].map(function(size){
34066             if (settings[size]) {
34067                 cfg.cls += ' col-' + size + '-' + settings[size];
34068             }
34069         });
34070         
34071         return cfg;
34072         
34073     },
34074
34075     initEvents : function()
34076     {
34077         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34078         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34079         
34080         if(!this.fieldLabel.length){
34081             this.labelEl.hide();
34082         }
34083         
34084         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34085         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34086         
34087         this.indicator = this.indicatorEl();
34088         
34089         if(this.indicator){
34090             this.indicator.addClass('invisible');
34091         }
34092         
34093         this.originalValue = this.getValue();
34094         
34095     },
34096     
34097     inputEl: function ()
34098     {
34099         return this.el.select('.roo-radio-set-input', true).first();
34100     },
34101     
34102     getChildContainer : function()
34103     {
34104         return this.itemsEl;
34105     },
34106     
34107     register : function(item)
34108     {
34109         this.radioes.push(item);
34110         
34111     },
34112     
34113     validate : function()
34114     {   
34115         if(this.getVisibilityEl().hasClass('hidden')){
34116             return true;
34117         }
34118         
34119         var valid = false;
34120         
34121         Roo.each(this.radioes, function(i){
34122             if(!i.checked){
34123                 return;
34124             }
34125             
34126             valid = true;
34127             return false;
34128         });
34129         
34130         if(this.allowBlank) {
34131             return true;
34132         }
34133         
34134         if(this.disabled || valid){
34135             this.markValid();
34136             return true;
34137         }
34138         
34139         this.markInvalid();
34140         return false;
34141         
34142     },
34143     
34144     markValid : function()
34145     {
34146         if(this.labelEl.isVisible(true)){
34147             this.indicatorEl().removeClass('visible');
34148             this.indicatorEl().addClass('invisible');
34149         }
34150         
34151         this.el.removeClass([this.invalidClass, this.validClass]);
34152         this.el.addClass(this.validClass);
34153         
34154         this.fireEvent('valid', this);
34155     },
34156     
34157     markInvalid : function(msg)
34158     {
34159         if(this.allowBlank || this.disabled){
34160             return;
34161         }
34162         
34163         if(this.labelEl.isVisible(true)){
34164             this.indicatorEl().removeClass('invisible');
34165             this.indicatorEl().addClass('visible');
34166         }
34167         
34168         this.el.removeClass([this.invalidClass, this.validClass]);
34169         this.el.addClass(this.invalidClass);
34170         
34171         this.fireEvent('invalid', this, msg);
34172         
34173     },
34174     
34175     setValue : function(v, suppressEvent)
34176     {   
34177         if(this.value === v){
34178             return;
34179         }
34180         
34181         this.value = v;
34182         
34183         if(this.rendered){
34184             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34185         }
34186         
34187         Roo.each(this.radioes, function(i){
34188             i.checked = false;
34189             i.el.removeClass('checked');
34190         });
34191         
34192         Roo.each(this.radioes, function(i){
34193             
34194             if(i.value === v || i.value.toString() === v.toString()){
34195                 i.checked = true;
34196                 i.el.addClass('checked');
34197                 
34198                 if(suppressEvent !== true){
34199                     this.fireEvent('check', this, i);
34200                 }
34201                 
34202                 return false;
34203             }
34204             
34205         }, this);
34206         
34207         this.validate();
34208     },
34209     
34210     clearInvalid : function(){
34211         
34212         if(!this.el || this.preventMark){
34213             return;
34214         }
34215         
34216         this.el.removeClass([this.invalidClass]);
34217         
34218         this.fireEvent('valid', this);
34219     }
34220     
34221 });
34222
34223 Roo.apply(Roo.bootstrap.RadioSet, {
34224     
34225     groups: {},
34226     
34227     register : function(set)
34228     {
34229         this.groups[set.name] = set;
34230     },
34231     
34232     get: function(name) 
34233     {
34234         if (typeof(this.groups[name]) == 'undefined') {
34235             return false;
34236         }
34237         
34238         return this.groups[name] ;
34239     }
34240     
34241 });
34242 /*
34243  * Based on:
34244  * Ext JS Library 1.1.1
34245  * Copyright(c) 2006-2007, Ext JS, LLC.
34246  *
34247  * Originally Released Under LGPL - original licence link has changed is not relivant.
34248  *
34249  * Fork - LGPL
34250  * <script type="text/javascript">
34251  */
34252
34253
34254 /**
34255  * @class Roo.bootstrap.SplitBar
34256  * @extends Roo.util.Observable
34257  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34258  * <br><br>
34259  * Usage:
34260  * <pre><code>
34261 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34262                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34263 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34264 split.minSize = 100;
34265 split.maxSize = 600;
34266 split.animate = true;
34267 split.on('moved', splitterMoved);
34268 </code></pre>
34269  * @constructor
34270  * Create a new SplitBar
34271  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34272  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34273  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34274  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34275                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34276                         position of the SplitBar).
34277  */
34278 Roo.bootstrap.SplitBar = function(cfg){
34279     
34280     /** @private */
34281     
34282     //{
34283     //  dragElement : elm
34284     //  resizingElement: el,
34285         // optional..
34286     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34287     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34288         // existingProxy ???
34289     //}
34290     
34291     this.el = Roo.get(cfg.dragElement, true);
34292     this.el.dom.unselectable = "on";
34293     /** @private */
34294     this.resizingEl = Roo.get(cfg.resizingElement, true);
34295
34296     /**
34297      * @private
34298      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34299      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34300      * @type Number
34301      */
34302     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34303     
34304     /**
34305      * The minimum size of the resizing element. (Defaults to 0)
34306      * @type Number
34307      */
34308     this.minSize = 0;
34309     
34310     /**
34311      * The maximum size of the resizing element. (Defaults to 2000)
34312      * @type Number
34313      */
34314     this.maxSize = 2000;
34315     
34316     /**
34317      * Whether to animate the transition to the new size
34318      * @type Boolean
34319      */
34320     this.animate = false;
34321     
34322     /**
34323      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34324      * @type Boolean
34325      */
34326     this.useShim = false;
34327     
34328     /** @private */
34329     this.shim = null;
34330     
34331     if(!cfg.existingProxy){
34332         /** @private */
34333         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34334     }else{
34335         this.proxy = Roo.get(cfg.existingProxy).dom;
34336     }
34337     /** @private */
34338     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34339     
34340     /** @private */
34341     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34342     
34343     /** @private */
34344     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34345     
34346     /** @private */
34347     this.dragSpecs = {};
34348     
34349     /**
34350      * @private The adapter to use to positon and resize elements
34351      */
34352     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34353     this.adapter.init(this);
34354     
34355     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34356         /** @private */
34357         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34358         this.el.addClass("roo-splitbar-h");
34359     }else{
34360         /** @private */
34361         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34362         this.el.addClass("roo-splitbar-v");
34363     }
34364     
34365     this.addEvents({
34366         /**
34367          * @event resize
34368          * Fires when the splitter is moved (alias for {@link #event-moved})
34369          * @param {Roo.bootstrap.SplitBar} this
34370          * @param {Number} newSize the new width or height
34371          */
34372         "resize" : true,
34373         /**
34374          * @event moved
34375          * Fires when the splitter is moved
34376          * @param {Roo.bootstrap.SplitBar} this
34377          * @param {Number} newSize the new width or height
34378          */
34379         "moved" : true,
34380         /**
34381          * @event beforeresize
34382          * Fires before the splitter is dragged
34383          * @param {Roo.bootstrap.SplitBar} this
34384          */
34385         "beforeresize" : true,
34386
34387         "beforeapply" : true
34388     });
34389
34390     Roo.util.Observable.call(this);
34391 };
34392
34393 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34394     onStartProxyDrag : function(x, y){
34395         this.fireEvent("beforeresize", this);
34396         if(!this.overlay){
34397             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34398             o.unselectable();
34399             o.enableDisplayMode("block");
34400             // all splitbars share the same overlay
34401             Roo.bootstrap.SplitBar.prototype.overlay = o;
34402         }
34403         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34404         this.overlay.show();
34405         Roo.get(this.proxy).setDisplayed("block");
34406         var size = this.adapter.getElementSize(this);
34407         this.activeMinSize = this.getMinimumSize();;
34408         this.activeMaxSize = this.getMaximumSize();;
34409         var c1 = size - this.activeMinSize;
34410         var c2 = Math.max(this.activeMaxSize - size, 0);
34411         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34412             this.dd.resetConstraints();
34413             this.dd.setXConstraint(
34414                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34415                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34416             );
34417             this.dd.setYConstraint(0, 0);
34418         }else{
34419             this.dd.resetConstraints();
34420             this.dd.setXConstraint(0, 0);
34421             this.dd.setYConstraint(
34422                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34423                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34424             );
34425          }
34426         this.dragSpecs.startSize = size;
34427         this.dragSpecs.startPoint = [x, y];
34428         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34429     },
34430     
34431     /** 
34432      * @private Called after the drag operation by the DDProxy
34433      */
34434     onEndProxyDrag : function(e){
34435         Roo.get(this.proxy).setDisplayed(false);
34436         var endPoint = Roo.lib.Event.getXY(e);
34437         if(this.overlay){
34438             this.overlay.hide();
34439         }
34440         var newSize;
34441         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34442             newSize = this.dragSpecs.startSize + 
34443                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34444                     endPoint[0] - this.dragSpecs.startPoint[0] :
34445                     this.dragSpecs.startPoint[0] - endPoint[0]
34446                 );
34447         }else{
34448             newSize = this.dragSpecs.startSize + 
34449                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34450                     endPoint[1] - this.dragSpecs.startPoint[1] :
34451                     this.dragSpecs.startPoint[1] - endPoint[1]
34452                 );
34453         }
34454         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34455         if(newSize != this.dragSpecs.startSize){
34456             if(this.fireEvent('beforeapply', this, newSize) !== false){
34457                 this.adapter.setElementSize(this, newSize);
34458                 this.fireEvent("moved", this, newSize);
34459                 this.fireEvent("resize", this, newSize);
34460             }
34461         }
34462     },
34463     
34464     /**
34465      * Get the adapter this SplitBar uses
34466      * @return The adapter object
34467      */
34468     getAdapter : function(){
34469         return this.adapter;
34470     },
34471     
34472     /**
34473      * Set the adapter this SplitBar uses
34474      * @param {Object} adapter A SplitBar adapter object
34475      */
34476     setAdapter : function(adapter){
34477         this.adapter = adapter;
34478         this.adapter.init(this);
34479     },
34480     
34481     /**
34482      * Gets the minimum size for the resizing element
34483      * @return {Number} The minimum size
34484      */
34485     getMinimumSize : function(){
34486         return this.minSize;
34487     },
34488     
34489     /**
34490      * Sets the minimum size for the resizing element
34491      * @param {Number} minSize The minimum size
34492      */
34493     setMinimumSize : function(minSize){
34494         this.minSize = minSize;
34495     },
34496     
34497     /**
34498      * Gets the maximum size for the resizing element
34499      * @return {Number} The maximum size
34500      */
34501     getMaximumSize : function(){
34502         return this.maxSize;
34503     },
34504     
34505     /**
34506      * Sets the maximum size for the resizing element
34507      * @param {Number} maxSize The maximum size
34508      */
34509     setMaximumSize : function(maxSize){
34510         this.maxSize = maxSize;
34511     },
34512     
34513     /**
34514      * Sets the initialize size for the resizing element
34515      * @param {Number} size The initial size
34516      */
34517     setCurrentSize : function(size){
34518         var oldAnimate = this.animate;
34519         this.animate = false;
34520         this.adapter.setElementSize(this, size);
34521         this.animate = oldAnimate;
34522     },
34523     
34524     /**
34525      * Destroy this splitbar. 
34526      * @param {Boolean} removeEl True to remove the element
34527      */
34528     destroy : function(removeEl){
34529         if(this.shim){
34530             this.shim.remove();
34531         }
34532         this.dd.unreg();
34533         this.proxy.parentNode.removeChild(this.proxy);
34534         if(removeEl){
34535             this.el.remove();
34536         }
34537     }
34538 });
34539
34540 /**
34541  * @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.
34542  */
34543 Roo.bootstrap.SplitBar.createProxy = function(dir){
34544     var proxy = new Roo.Element(document.createElement("div"));
34545     proxy.unselectable();
34546     var cls = 'roo-splitbar-proxy';
34547     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34548     document.body.appendChild(proxy.dom);
34549     return proxy.dom;
34550 };
34551
34552 /** 
34553  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34554  * Default Adapter. It assumes the splitter and resizing element are not positioned
34555  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34556  */
34557 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34558 };
34559
34560 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34561     // do nothing for now
34562     init : function(s){
34563     
34564     },
34565     /**
34566      * Called before drag operations to get the current size of the resizing element. 
34567      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34568      */
34569      getElementSize : function(s){
34570         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34571             return s.resizingEl.getWidth();
34572         }else{
34573             return s.resizingEl.getHeight();
34574         }
34575     },
34576     
34577     /**
34578      * Called after drag operations to set the size of the resizing element.
34579      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34580      * @param {Number} newSize The new size to set
34581      * @param {Function} onComplete A function to be invoked when resizing is complete
34582      */
34583     setElementSize : function(s, newSize, onComplete){
34584         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34585             if(!s.animate){
34586                 s.resizingEl.setWidth(newSize);
34587                 if(onComplete){
34588                     onComplete(s, newSize);
34589                 }
34590             }else{
34591                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34592             }
34593         }else{
34594             
34595             if(!s.animate){
34596                 s.resizingEl.setHeight(newSize);
34597                 if(onComplete){
34598                     onComplete(s, newSize);
34599                 }
34600             }else{
34601                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34602             }
34603         }
34604     }
34605 };
34606
34607 /** 
34608  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34609  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34610  * Adapter that  moves the splitter element to align with the resized sizing element. 
34611  * Used with an absolute positioned SplitBar.
34612  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34613  * document.body, make sure you assign an id to the body element.
34614  */
34615 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34616     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34617     this.container = Roo.get(container);
34618 };
34619
34620 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34621     init : function(s){
34622         this.basic.init(s);
34623     },
34624     
34625     getElementSize : function(s){
34626         return this.basic.getElementSize(s);
34627     },
34628     
34629     setElementSize : function(s, newSize, onComplete){
34630         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34631     },
34632     
34633     moveSplitter : function(s){
34634         var yes = Roo.bootstrap.SplitBar;
34635         switch(s.placement){
34636             case yes.LEFT:
34637                 s.el.setX(s.resizingEl.getRight());
34638                 break;
34639             case yes.RIGHT:
34640                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34641                 break;
34642             case yes.TOP:
34643                 s.el.setY(s.resizingEl.getBottom());
34644                 break;
34645             case yes.BOTTOM:
34646                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34647                 break;
34648         }
34649     }
34650 };
34651
34652 /**
34653  * Orientation constant - Create a vertical SplitBar
34654  * @static
34655  * @type Number
34656  */
34657 Roo.bootstrap.SplitBar.VERTICAL = 1;
34658
34659 /**
34660  * Orientation constant - Create a horizontal SplitBar
34661  * @static
34662  * @type Number
34663  */
34664 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34665
34666 /**
34667  * Placement constant - The resizing element is to the left of the splitter element
34668  * @static
34669  * @type Number
34670  */
34671 Roo.bootstrap.SplitBar.LEFT = 1;
34672
34673 /**
34674  * Placement constant - The resizing element is to the right of the splitter element
34675  * @static
34676  * @type Number
34677  */
34678 Roo.bootstrap.SplitBar.RIGHT = 2;
34679
34680 /**
34681  * Placement constant - The resizing element is positioned above the splitter element
34682  * @static
34683  * @type Number
34684  */
34685 Roo.bootstrap.SplitBar.TOP = 3;
34686
34687 /**
34688  * Placement constant - The resizing element is positioned under splitter element
34689  * @static
34690  * @type Number
34691  */
34692 Roo.bootstrap.SplitBar.BOTTOM = 4;
34693 Roo.namespace("Roo.bootstrap.layout");/*
34694  * Based on:
34695  * Ext JS Library 1.1.1
34696  * Copyright(c) 2006-2007, Ext JS, LLC.
34697  *
34698  * Originally Released Under LGPL - original licence link has changed is not relivant.
34699  *
34700  * Fork - LGPL
34701  * <script type="text/javascript">
34702  */
34703
34704 /**
34705  * @class Roo.bootstrap.layout.Manager
34706  * @extends Roo.bootstrap.Component
34707  * Base class for layout managers.
34708  */
34709 Roo.bootstrap.layout.Manager = function(config)
34710 {
34711     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34712
34713
34714
34715
34716
34717     /** false to disable window resize monitoring @type Boolean */
34718     this.monitorWindowResize = true;
34719     this.regions = {};
34720     this.addEvents({
34721         /**
34722          * @event layout
34723          * Fires when a layout is performed.
34724          * @param {Roo.LayoutManager} this
34725          */
34726         "layout" : true,
34727         /**
34728          * @event regionresized
34729          * Fires when the user resizes a region.
34730          * @param {Roo.LayoutRegion} region The resized region
34731          * @param {Number} newSize The new size (width for east/west, height for north/south)
34732          */
34733         "regionresized" : true,
34734         /**
34735          * @event regioncollapsed
34736          * Fires when a region is collapsed.
34737          * @param {Roo.LayoutRegion} region The collapsed region
34738          */
34739         "regioncollapsed" : true,
34740         /**
34741          * @event regionexpanded
34742          * Fires when a region is expanded.
34743          * @param {Roo.LayoutRegion} region The expanded region
34744          */
34745         "regionexpanded" : true
34746     });
34747     this.updating = false;
34748
34749     if (config.el) {
34750         this.el = Roo.get(config.el);
34751         this.initEvents();
34752     }
34753
34754 };
34755
34756 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34757
34758
34759     regions : null,
34760
34761     monitorWindowResize : true,
34762
34763
34764     updating : false,
34765
34766
34767     onRender : function(ct, position)
34768     {
34769         if(!this.el){
34770             this.el = Roo.get(ct);
34771             this.initEvents();
34772         }
34773         //this.fireEvent('render',this);
34774     },
34775
34776
34777     initEvents: function()
34778     {
34779
34780
34781         // ie scrollbar fix
34782         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34783             document.body.scroll = "no";
34784         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34785             this.el.position('relative');
34786         }
34787         this.id = this.el.id;
34788         this.el.addClass("roo-layout-container");
34789         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34790         if(this.el.dom != document.body ) {
34791             this.el.on('resize', this.layout,this);
34792             this.el.on('show', this.layout,this);
34793         }
34794
34795     },
34796
34797     /**
34798      * Returns true if this layout is currently being updated
34799      * @return {Boolean}
34800      */
34801     isUpdating : function(){
34802         return this.updating;
34803     },
34804
34805     /**
34806      * Suspend the LayoutManager from doing auto-layouts while
34807      * making multiple add or remove calls
34808      */
34809     beginUpdate : function(){
34810         this.updating = true;
34811     },
34812
34813     /**
34814      * Restore auto-layouts and optionally disable the manager from performing a layout
34815      * @param {Boolean} noLayout true to disable a layout update
34816      */
34817     endUpdate : function(noLayout){
34818         this.updating = false;
34819         if(!noLayout){
34820             this.layout();
34821         }
34822     },
34823
34824     layout: function(){
34825         // abstract...
34826     },
34827
34828     onRegionResized : function(region, newSize){
34829         this.fireEvent("regionresized", region, newSize);
34830         this.layout();
34831     },
34832
34833     onRegionCollapsed : function(region){
34834         this.fireEvent("regioncollapsed", region);
34835     },
34836
34837     onRegionExpanded : function(region){
34838         this.fireEvent("regionexpanded", region);
34839     },
34840
34841     /**
34842      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34843      * performs box-model adjustments.
34844      * @return {Object} The size as an object {width: (the width), height: (the height)}
34845      */
34846     getViewSize : function()
34847     {
34848         var size;
34849         if(this.el.dom != document.body){
34850             size = this.el.getSize();
34851         }else{
34852             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34853         }
34854         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34855         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34856         return size;
34857     },
34858
34859     /**
34860      * Returns the Element this layout is bound to.
34861      * @return {Roo.Element}
34862      */
34863     getEl : function(){
34864         return this.el;
34865     },
34866
34867     /**
34868      * Returns the specified region.
34869      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34870      * @return {Roo.LayoutRegion}
34871      */
34872     getRegion : function(target){
34873         return this.regions[target.toLowerCase()];
34874     },
34875
34876     onWindowResize : function(){
34877         if(this.monitorWindowResize){
34878             this.layout();
34879         }
34880     }
34881 });
34882 /*
34883  * Based on:
34884  * Ext JS Library 1.1.1
34885  * Copyright(c) 2006-2007, Ext JS, LLC.
34886  *
34887  * Originally Released Under LGPL - original licence link has changed is not relivant.
34888  *
34889  * Fork - LGPL
34890  * <script type="text/javascript">
34891  */
34892 /**
34893  * @class Roo.bootstrap.layout.Border
34894  * @extends Roo.bootstrap.layout.Manager
34895  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34896  * please see: examples/bootstrap/nested.html<br><br>
34897  
34898 <b>The container the layout is rendered into can be either the body element or any other element.
34899 If it is not the body element, the container needs to either be an absolute positioned element,
34900 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34901 the container size if it is not the body element.</b>
34902
34903 * @constructor
34904 * Create a new Border
34905 * @param {Object} config Configuration options
34906  */
34907 Roo.bootstrap.layout.Border = function(config){
34908     config = config || {};
34909     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34910     
34911     
34912     
34913     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34914         if(config[region]){
34915             config[region].region = region;
34916             this.addRegion(config[region]);
34917         }
34918     },this);
34919     
34920 };
34921
34922 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34923
34924 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34925     /**
34926      * Creates and adds a new region if it doesn't already exist.
34927      * @param {String} target The target region key (north, south, east, west or center).
34928      * @param {Object} config The regions config object
34929      * @return {BorderLayoutRegion} The new region
34930      */
34931     addRegion : function(config)
34932     {
34933         if(!this.regions[config.region]){
34934             var r = this.factory(config);
34935             this.bindRegion(r);
34936         }
34937         return this.regions[config.region];
34938     },
34939
34940     // private (kinda)
34941     bindRegion : function(r){
34942         this.regions[r.config.region] = r;
34943         
34944         r.on("visibilitychange",    this.layout, this);
34945         r.on("paneladded",          this.layout, this);
34946         r.on("panelremoved",        this.layout, this);
34947         r.on("invalidated",         this.layout, this);
34948         r.on("resized",             this.onRegionResized, this);
34949         r.on("collapsed",           this.onRegionCollapsed, this);
34950         r.on("expanded",            this.onRegionExpanded, this);
34951     },
34952
34953     /**
34954      * Performs a layout update.
34955      */
34956     layout : function()
34957     {
34958         if(this.updating) {
34959             return;
34960         }
34961         
34962         // render all the rebions if they have not been done alreayd?
34963         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34964             if(this.regions[region] && !this.regions[region].bodyEl){
34965                 this.regions[region].onRender(this.el)
34966             }
34967         },this);
34968         
34969         var size = this.getViewSize();
34970         var w = size.width;
34971         var h = size.height;
34972         var centerW = w;
34973         var centerH = h;
34974         var centerY = 0;
34975         var centerX = 0;
34976         //var x = 0, y = 0;
34977
34978         var rs = this.regions;
34979         var north = rs["north"];
34980         var south = rs["south"]; 
34981         var west = rs["west"];
34982         var east = rs["east"];
34983         var center = rs["center"];
34984         //if(this.hideOnLayout){ // not supported anymore
34985             //c.el.setStyle("display", "none");
34986         //}
34987         if(north && north.isVisible()){
34988             var b = north.getBox();
34989             var m = north.getMargins();
34990             b.width = w - (m.left+m.right);
34991             b.x = m.left;
34992             b.y = m.top;
34993             centerY = b.height + b.y + m.bottom;
34994             centerH -= centerY;
34995             north.updateBox(this.safeBox(b));
34996         }
34997         if(south && south.isVisible()){
34998             var b = south.getBox();
34999             var m = south.getMargins();
35000             b.width = w - (m.left+m.right);
35001             b.x = m.left;
35002             var totalHeight = (b.height + m.top + m.bottom);
35003             b.y = h - totalHeight + m.top;
35004             centerH -= totalHeight;
35005             south.updateBox(this.safeBox(b));
35006         }
35007         if(west && west.isVisible()){
35008             var b = west.getBox();
35009             var m = west.getMargins();
35010             b.height = centerH - (m.top+m.bottom);
35011             b.x = m.left;
35012             b.y = centerY + m.top;
35013             var totalWidth = (b.width + m.left + m.right);
35014             centerX += totalWidth;
35015             centerW -= totalWidth;
35016             west.updateBox(this.safeBox(b));
35017         }
35018         if(east && east.isVisible()){
35019             var b = east.getBox();
35020             var m = east.getMargins();
35021             b.height = centerH - (m.top+m.bottom);
35022             var totalWidth = (b.width + m.left + m.right);
35023             b.x = w - totalWidth + m.left;
35024             b.y = centerY + m.top;
35025             centerW -= totalWidth;
35026             east.updateBox(this.safeBox(b));
35027         }
35028         if(center){
35029             var m = center.getMargins();
35030             var centerBox = {
35031                 x: centerX + m.left,
35032                 y: centerY + m.top,
35033                 width: centerW - (m.left+m.right),
35034                 height: centerH - (m.top+m.bottom)
35035             };
35036             //if(this.hideOnLayout){
35037                 //center.el.setStyle("display", "block");
35038             //}
35039             center.updateBox(this.safeBox(centerBox));
35040         }
35041         this.el.repaint();
35042         this.fireEvent("layout", this);
35043     },
35044
35045     // private
35046     safeBox : function(box){
35047         box.width = Math.max(0, box.width);
35048         box.height = Math.max(0, box.height);
35049         return box;
35050     },
35051
35052     /**
35053      * Adds a ContentPanel (or subclass) to this layout.
35054      * @param {String} target The target region key (north, south, east, west or center).
35055      * @param {Roo.ContentPanel} panel The panel to add
35056      * @return {Roo.ContentPanel} The added panel
35057      */
35058     add : function(target, panel){
35059          
35060         target = target.toLowerCase();
35061         return this.regions[target].add(panel);
35062     },
35063
35064     /**
35065      * Remove a ContentPanel (or subclass) to this layout.
35066      * @param {String} target The target region key (north, south, east, west or center).
35067      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35068      * @return {Roo.ContentPanel} The removed panel
35069      */
35070     remove : function(target, panel){
35071         target = target.toLowerCase();
35072         return this.regions[target].remove(panel);
35073     },
35074
35075     /**
35076      * Searches all regions for a panel with the specified id
35077      * @param {String} panelId
35078      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35079      */
35080     findPanel : function(panelId){
35081         var rs = this.regions;
35082         for(var target in rs){
35083             if(typeof rs[target] != "function"){
35084                 var p = rs[target].getPanel(panelId);
35085                 if(p){
35086                     return p;
35087                 }
35088             }
35089         }
35090         return null;
35091     },
35092
35093     /**
35094      * Searches all regions for a panel with the specified id and activates (shows) it.
35095      * @param {String/ContentPanel} panelId The panels id or the panel itself
35096      * @return {Roo.ContentPanel} The shown panel or null
35097      */
35098     showPanel : function(panelId) {
35099       var rs = this.regions;
35100       for(var target in rs){
35101          var r = rs[target];
35102          if(typeof r != "function"){
35103             if(r.hasPanel(panelId)){
35104                return r.showPanel(panelId);
35105             }
35106          }
35107       }
35108       return null;
35109    },
35110
35111    /**
35112      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35113      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35114      */
35115    /*
35116     restoreState : function(provider){
35117         if(!provider){
35118             provider = Roo.state.Manager;
35119         }
35120         var sm = new Roo.LayoutStateManager();
35121         sm.init(this, provider);
35122     },
35123 */
35124  
35125  
35126     /**
35127      * Adds a xtype elements to the layout.
35128      * <pre><code>
35129
35130 layout.addxtype({
35131        xtype : 'ContentPanel',
35132        region: 'west',
35133        items: [ .... ]
35134    }
35135 );
35136
35137 layout.addxtype({
35138         xtype : 'NestedLayoutPanel',
35139         region: 'west',
35140         layout: {
35141            center: { },
35142            west: { }   
35143         },
35144         items : [ ... list of content panels or nested layout panels.. ]
35145    }
35146 );
35147 </code></pre>
35148      * @param {Object} cfg Xtype definition of item to add.
35149      */
35150     addxtype : function(cfg)
35151     {
35152         // basically accepts a pannel...
35153         // can accept a layout region..!?!?
35154         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35155         
35156         
35157         // theory?  children can only be panels??
35158         
35159         //if (!cfg.xtype.match(/Panel$/)) {
35160         //    return false;
35161         //}
35162         var ret = false;
35163         
35164         if (typeof(cfg.region) == 'undefined') {
35165             Roo.log("Failed to add Panel, region was not set");
35166             Roo.log(cfg);
35167             return false;
35168         }
35169         var region = cfg.region;
35170         delete cfg.region;
35171         
35172           
35173         var xitems = [];
35174         if (cfg.items) {
35175             xitems = cfg.items;
35176             delete cfg.items;
35177         }
35178         var nb = false;
35179         
35180         switch(cfg.xtype) 
35181         {
35182             case 'Content':  // ContentPanel (el, cfg)
35183             case 'Scroll':  // ContentPanel (el, cfg)
35184             case 'View': 
35185                 cfg.autoCreate = true;
35186                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35187                 //} else {
35188                 //    var el = this.el.createChild();
35189                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35190                 //}
35191                 
35192                 this.add(region, ret);
35193                 break;
35194             
35195             /*
35196             case 'TreePanel': // our new panel!
35197                 cfg.el = this.el.createChild();
35198                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35199                 this.add(region, ret);
35200                 break;
35201             */
35202             
35203             case 'Nest': 
35204                 // create a new Layout (which is  a Border Layout...
35205                 
35206                 var clayout = cfg.layout;
35207                 clayout.el  = this.el.createChild();
35208                 clayout.items   = clayout.items  || [];
35209                 
35210                 delete cfg.layout;
35211                 
35212                 // replace this exitems with the clayout ones..
35213                 xitems = clayout.items;
35214                  
35215                 // force background off if it's in center...
35216                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35217                     cfg.background = false;
35218                 }
35219                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35220                 
35221                 
35222                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35223                 //console.log('adding nested layout panel '  + cfg.toSource());
35224                 this.add(region, ret);
35225                 nb = {}; /// find first...
35226                 break;
35227             
35228             case 'Grid':
35229                 
35230                 // needs grid and region
35231                 
35232                 //var el = this.getRegion(region).el.createChild();
35233                 /*
35234                  *var el = this.el.createChild();
35235                 // create the grid first...
35236                 cfg.grid.container = el;
35237                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35238                 */
35239                 
35240                 if (region == 'center' && this.active ) {
35241                     cfg.background = false;
35242                 }
35243                 
35244                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35245                 
35246                 this.add(region, ret);
35247                 /*
35248                 if (cfg.background) {
35249                     // render grid on panel activation (if panel background)
35250                     ret.on('activate', function(gp) {
35251                         if (!gp.grid.rendered) {
35252                     //        gp.grid.render(el);
35253                         }
35254                     });
35255                 } else {
35256                   //  cfg.grid.render(el);
35257                 }
35258                 */
35259                 break;
35260            
35261            
35262             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35263                 // it was the old xcomponent building that caused this before.
35264                 // espeically if border is the top element in the tree.
35265                 ret = this;
35266                 break; 
35267                 
35268                     
35269                 
35270                 
35271                 
35272             default:
35273                 /*
35274                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35275                     
35276                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35277                     this.add(region, ret);
35278                 } else {
35279                 */
35280                     Roo.log(cfg);
35281                     throw "Can not add '" + cfg.xtype + "' to Border";
35282                     return null;
35283              
35284                                 
35285              
35286         }
35287         this.beginUpdate();
35288         // add children..
35289         var region = '';
35290         var abn = {};
35291         Roo.each(xitems, function(i)  {
35292             region = nb && i.region ? i.region : false;
35293             
35294             var add = ret.addxtype(i);
35295            
35296             if (region) {
35297                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35298                 if (!i.background) {
35299                     abn[region] = nb[region] ;
35300                 }
35301             }
35302             
35303         });
35304         this.endUpdate();
35305
35306         // make the last non-background panel active..
35307         //if (nb) { Roo.log(abn); }
35308         if (nb) {
35309             
35310             for(var r in abn) {
35311                 region = this.getRegion(r);
35312                 if (region) {
35313                     // tried using nb[r], but it does not work..
35314                      
35315                     region.showPanel(abn[r]);
35316                    
35317                 }
35318             }
35319         }
35320         return ret;
35321         
35322     },
35323     
35324     
35325 // private
35326     factory : function(cfg)
35327     {
35328         
35329         var validRegions = Roo.bootstrap.layout.Border.regions;
35330
35331         var target = cfg.region;
35332         cfg.mgr = this;
35333         
35334         var r = Roo.bootstrap.layout;
35335         Roo.log(target);
35336         switch(target){
35337             case "north":
35338                 return new r.North(cfg);
35339             case "south":
35340                 return new r.South(cfg);
35341             case "east":
35342                 return new r.East(cfg);
35343             case "west":
35344                 return new r.West(cfg);
35345             case "center":
35346                 return new r.Center(cfg);
35347         }
35348         throw 'Layout region "'+target+'" not supported.';
35349     }
35350     
35351     
35352 });
35353  /*
35354  * Based on:
35355  * Ext JS Library 1.1.1
35356  * Copyright(c) 2006-2007, Ext JS, LLC.
35357  *
35358  * Originally Released Under LGPL - original licence link has changed is not relivant.
35359  *
35360  * Fork - LGPL
35361  * <script type="text/javascript">
35362  */
35363  
35364 /**
35365  * @class Roo.bootstrap.layout.Basic
35366  * @extends Roo.util.Observable
35367  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35368  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35369  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35370  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35371  * @cfg {string}   region  the region that it inhabits..
35372  * @cfg {bool}   skipConfig skip config?
35373  * 
35374
35375  */
35376 Roo.bootstrap.layout.Basic = function(config){
35377     
35378     this.mgr = config.mgr;
35379     
35380     this.position = config.region;
35381     
35382     var skipConfig = config.skipConfig;
35383     
35384     this.events = {
35385         /**
35386          * @scope Roo.BasicLayoutRegion
35387          */
35388         
35389         /**
35390          * @event beforeremove
35391          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35392          * @param {Roo.LayoutRegion} this
35393          * @param {Roo.ContentPanel} panel The panel
35394          * @param {Object} e The cancel event object
35395          */
35396         "beforeremove" : true,
35397         /**
35398          * @event invalidated
35399          * Fires when the layout for this region is changed.
35400          * @param {Roo.LayoutRegion} this
35401          */
35402         "invalidated" : true,
35403         /**
35404          * @event visibilitychange
35405          * Fires when this region is shown or hidden 
35406          * @param {Roo.LayoutRegion} this
35407          * @param {Boolean} visibility true or false
35408          */
35409         "visibilitychange" : true,
35410         /**
35411          * @event paneladded
35412          * Fires when a panel is added. 
35413          * @param {Roo.LayoutRegion} this
35414          * @param {Roo.ContentPanel} panel The panel
35415          */
35416         "paneladded" : true,
35417         /**
35418          * @event panelremoved
35419          * Fires when a panel is removed. 
35420          * @param {Roo.LayoutRegion} this
35421          * @param {Roo.ContentPanel} panel The panel
35422          */
35423         "panelremoved" : true,
35424         /**
35425          * @event beforecollapse
35426          * Fires when this region before collapse.
35427          * @param {Roo.LayoutRegion} this
35428          */
35429         "beforecollapse" : true,
35430         /**
35431          * @event collapsed
35432          * Fires when this region is collapsed.
35433          * @param {Roo.LayoutRegion} this
35434          */
35435         "collapsed" : true,
35436         /**
35437          * @event expanded
35438          * Fires when this region is expanded.
35439          * @param {Roo.LayoutRegion} this
35440          */
35441         "expanded" : true,
35442         /**
35443          * @event slideshow
35444          * Fires when this region is slid into view.
35445          * @param {Roo.LayoutRegion} this
35446          */
35447         "slideshow" : true,
35448         /**
35449          * @event slidehide
35450          * Fires when this region slides out of view. 
35451          * @param {Roo.LayoutRegion} this
35452          */
35453         "slidehide" : true,
35454         /**
35455          * @event panelactivated
35456          * Fires when a panel is activated. 
35457          * @param {Roo.LayoutRegion} this
35458          * @param {Roo.ContentPanel} panel The activated panel
35459          */
35460         "panelactivated" : true,
35461         /**
35462          * @event resized
35463          * Fires when the user resizes this region. 
35464          * @param {Roo.LayoutRegion} this
35465          * @param {Number} newSize The new size (width for east/west, height for north/south)
35466          */
35467         "resized" : true
35468     };
35469     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35470     this.panels = new Roo.util.MixedCollection();
35471     this.panels.getKey = this.getPanelId.createDelegate(this);
35472     this.box = null;
35473     this.activePanel = null;
35474     // ensure listeners are added...
35475     
35476     if (config.listeners || config.events) {
35477         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35478             listeners : config.listeners || {},
35479             events : config.events || {}
35480         });
35481     }
35482     
35483     if(skipConfig !== true){
35484         this.applyConfig(config);
35485     }
35486 };
35487
35488 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35489 {
35490     getPanelId : function(p){
35491         return p.getId();
35492     },
35493     
35494     applyConfig : function(config){
35495         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35496         this.config = config;
35497         
35498     },
35499     
35500     /**
35501      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35502      * the width, for horizontal (north, south) the height.
35503      * @param {Number} newSize The new width or height
35504      */
35505     resizeTo : function(newSize){
35506         var el = this.el ? this.el :
35507                  (this.activePanel ? this.activePanel.getEl() : null);
35508         if(el){
35509             switch(this.position){
35510                 case "east":
35511                 case "west":
35512                     el.setWidth(newSize);
35513                     this.fireEvent("resized", this, newSize);
35514                 break;
35515                 case "north":
35516                 case "south":
35517                     el.setHeight(newSize);
35518                     this.fireEvent("resized", this, newSize);
35519                 break;                
35520             }
35521         }
35522     },
35523     
35524     getBox : function(){
35525         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35526     },
35527     
35528     getMargins : function(){
35529         return this.margins;
35530     },
35531     
35532     updateBox : function(box){
35533         this.box = box;
35534         var el = this.activePanel.getEl();
35535         el.dom.style.left = box.x + "px";
35536         el.dom.style.top = box.y + "px";
35537         this.activePanel.setSize(box.width, box.height);
35538     },
35539     
35540     /**
35541      * Returns the container element for this region.
35542      * @return {Roo.Element}
35543      */
35544     getEl : function(){
35545         return this.activePanel;
35546     },
35547     
35548     /**
35549      * Returns true if this region is currently visible.
35550      * @return {Boolean}
35551      */
35552     isVisible : function(){
35553         return this.activePanel ? true : false;
35554     },
35555     
35556     setActivePanel : function(panel){
35557         panel = this.getPanel(panel);
35558         if(this.activePanel && this.activePanel != panel){
35559             this.activePanel.setActiveState(false);
35560             this.activePanel.getEl().setLeftTop(-10000,-10000);
35561         }
35562         this.activePanel = panel;
35563         panel.setActiveState(true);
35564         if(this.box){
35565             panel.setSize(this.box.width, this.box.height);
35566         }
35567         this.fireEvent("panelactivated", this, panel);
35568         this.fireEvent("invalidated");
35569     },
35570     
35571     /**
35572      * Show the specified panel.
35573      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35574      * @return {Roo.ContentPanel} The shown panel or null
35575      */
35576     showPanel : function(panel){
35577         panel = this.getPanel(panel);
35578         if(panel){
35579             this.setActivePanel(panel);
35580         }
35581         return panel;
35582     },
35583     
35584     /**
35585      * Get the active panel for this region.
35586      * @return {Roo.ContentPanel} The active panel or null
35587      */
35588     getActivePanel : function(){
35589         return this.activePanel;
35590     },
35591     
35592     /**
35593      * Add the passed ContentPanel(s)
35594      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35595      * @return {Roo.ContentPanel} The panel added (if only one was added)
35596      */
35597     add : function(panel){
35598         if(arguments.length > 1){
35599             for(var i = 0, len = arguments.length; i < len; i++) {
35600                 this.add(arguments[i]);
35601             }
35602             return null;
35603         }
35604         if(this.hasPanel(panel)){
35605             this.showPanel(panel);
35606             return panel;
35607         }
35608         var el = panel.getEl();
35609         if(el.dom.parentNode != this.mgr.el.dom){
35610             this.mgr.el.dom.appendChild(el.dom);
35611         }
35612         if(panel.setRegion){
35613             panel.setRegion(this);
35614         }
35615         this.panels.add(panel);
35616         el.setStyle("position", "absolute");
35617         if(!panel.background){
35618             this.setActivePanel(panel);
35619             if(this.config.initialSize && this.panels.getCount()==1){
35620                 this.resizeTo(this.config.initialSize);
35621             }
35622         }
35623         this.fireEvent("paneladded", this, panel);
35624         return panel;
35625     },
35626     
35627     /**
35628      * Returns true if the panel is in this region.
35629      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35630      * @return {Boolean}
35631      */
35632     hasPanel : function(panel){
35633         if(typeof panel == "object"){ // must be panel obj
35634             panel = panel.getId();
35635         }
35636         return this.getPanel(panel) ? true : false;
35637     },
35638     
35639     /**
35640      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35641      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35642      * @param {Boolean} preservePanel Overrides the config preservePanel option
35643      * @return {Roo.ContentPanel} The panel that was removed
35644      */
35645     remove : function(panel, preservePanel){
35646         panel = this.getPanel(panel);
35647         if(!panel){
35648             return null;
35649         }
35650         var e = {};
35651         this.fireEvent("beforeremove", this, panel, e);
35652         if(e.cancel === true){
35653             return null;
35654         }
35655         var panelId = panel.getId();
35656         this.panels.removeKey(panelId);
35657         return panel;
35658     },
35659     
35660     /**
35661      * Returns the panel specified or null if it's not in this region.
35662      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35663      * @return {Roo.ContentPanel}
35664      */
35665     getPanel : function(id){
35666         if(typeof id == "object"){ // must be panel obj
35667             return id;
35668         }
35669         return this.panels.get(id);
35670     },
35671     
35672     /**
35673      * Returns this regions position (north/south/east/west/center).
35674      * @return {String} 
35675      */
35676     getPosition: function(){
35677         return this.position;    
35678     }
35679 });/*
35680  * Based on:
35681  * Ext JS Library 1.1.1
35682  * Copyright(c) 2006-2007, Ext JS, LLC.
35683  *
35684  * Originally Released Under LGPL - original licence link has changed is not relivant.
35685  *
35686  * Fork - LGPL
35687  * <script type="text/javascript">
35688  */
35689  
35690 /**
35691  * @class Roo.bootstrap.layout.Region
35692  * @extends Roo.bootstrap.layout.Basic
35693  * This class represents a region in a layout manager.
35694  
35695  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35696  * @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})
35697  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35698  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35699  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35700  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35701  * @cfg {String}    title           The title for the region (overrides panel titles)
35702  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35703  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35704  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35705  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35706  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35707  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35708  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35709  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35710  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35711  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35712
35713  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35714  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35715  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35716  * @cfg {Number}    width           For East/West panels
35717  * @cfg {Number}    height          For North/South panels
35718  * @cfg {Boolean}   split           To show the splitter
35719  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35720  * 
35721  * @cfg {string}   cls             Extra CSS classes to add to region
35722  * 
35723  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35724  * @cfg {string}   region  the region that it inhabits..
35725  *
35726
35727  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35728  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35729
35730  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35731  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35732  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35733  */
35734 Roo.bootstrap.layout.Region = function(config)
35735 {
35736     this.applyConfig(config);
35737
35738     var mgr = config.mgr;
35739     var pos = config.region;
35740     config.skipConfig = true;
35741     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35742     
35743     if (mgr.el) {
35744         this.onRender(mgr.el);   
35745     }
35746      
35747     this.visible = true;
35748     this.collapsed = false;
35749     this.unrendered_panels = [];
35750 };
35751
35752 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35753
35754     position: '', // set by wrapper (eg. north/south etc..)
35755     unrendered_panels : null,  // unrendered panels.
35756     createBody : function(){
35757         /** This region's body element 
35758         * @type Roo.Element */
35759         this.bodyEl = this.el.createChild({
35760                 tag: "div",
35761                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35762         });
35763     },
35764
35765     onRender: function(ctr, pos)
35766     {
35767         var dh = Roo.DomHelper;
35768         /** This region's container element 
35769         * @type Roo.Element */
35770         this.el = dh.append(ctr.dom, {
35771                 tag: "div",
35772                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35773             }, true);
35774         /** This region's title element 
35775         * @type Roo.Element */
35776     
35777         this.titleEl = dh.append(this.el.dom,
35778             {
35779                     tag: "div",
35780                     unselectable: "on",
35781                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35782                     children:[
35783                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35784                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35785                     ]}, true);
35786         
35787         this.titleEl.enableDisplayMode();
35788         /** This region's title text element 
35789         * @type HTMLElement */
35790         this.titleTextEl = this.titleEl.dom.firstChild;
35791         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35792         /*
35793         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35794         this.closeBtn.enableDisplayMode();
35795         this.closeBtn.on("click", this.closeClicked, this);
35796         this.closeBtn.hide();
35797     */
35798         this.createBody(this.config);
35799         if(this.config.hideWhenEmpty){
35800             this.hide();
35801             this.on("paneladded", this.validateVisibility, this);
35802             this.on("panelremoved", this.validateVisibility, this);
35803         }
35804         if(this.autoScroll){
35805             this.bodyEl.setStyle("overflow", "auto");
35806         }else{
35807             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35808         }
35809         //if(c.titlebar !== false){
35810             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35811                 this.titleEl.hide();
35812             }else{
35813                 this.titleEl.show();
35814                 if(this.config.title){
35815                     this.titleTextEl.innerHTML = this.config.title;
35816                 }
35817             }
35818         //}
35819         if(this.config.collapsed){
35820             this.collapse(true);
35821         }
35822         if(this.config.hidden){
35823             this.hide();
35824         }
35825         
35826         if (this.unrendered_panels && this.unrendered_panels.length) {
35827             for (var i =0;i< this.unrendered_panels.length; i++) {
35828                 this.add(this.unrendered_panels[i]);
35829             }
35830             this.unrendered_panels = null;
35831             
35832         }
35833         
35834     },
35835     
35836     applyConfig : function(c)
35837     {
35838         /*
35839          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35840             var dh = Roo.DomHelper;
35841             if(c.titlebar !== false){
35842                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35843                 this.collapseBtn.on("click", this.collapse, this);
35844                 this.collapseBtn.enableDisplayMode();
35845                 /*
35846                 if(c.showPin === true || this.showPin){
35847                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35848                     this.stickBtn.enableDisplayMode();
35849                     this.stickBtn.on("click", this.expand, this);
35850                     this.stickBtn.hide();
35851                 }
35852                 
35853             }
35854             */
35855             /** This region's collapsed element
35856             * @type Roo.Element */
35857             /*
35858              *
35859             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35860                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35861             ]}, true);
35862             
35863             if(c.floatable !== false){
35864                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35865                this.collapsedEl.on("click", this.collapseClick, this);
35866             }
35867
35868             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35869                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35870                    id: "message", unselectable: "on", style:{"float":"left"}});
35871                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35872              }
35873             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35874             this.expandBtn.on("click", this.expand, this);
35875             
35876         }
35877         
35878         if(this.collapseBtn){
35879             this.collapseBtn.setVisible(c.collapsible == true);
35880         }
35881         
35882         this.cmargins = c.cmargins || this.cmargins ||
35883                          (this.position == "west" || this.position == "east" ?
35884                              {top: 0, left: 2, right:2, bottom: 0} :
35885                              {top: 2, left: 0, right:0, bottom: 2});
35886         */
35887         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35888         
35889         
35890         this.bottomTabs = c.tabPosition != "top";
35891         
35892         this.autoScroll = c.autoScroll || false;
35893         
35894         
35895        
35896         
35897         this.duration = c.duration || .30;
35898         this.slideDuration = c.slideDuration || .45;
35899         this.config = c;
35900        
35901     },
35902     /**
35903      * Returns true if this region is currently visible.
35904      * @return {Boolean}
35905      */
35906     isVisible : function(){
35907         return this.visible;
35908     },
35909
35910     /**
35911      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35912      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35913      */
35914     //setCollapsedTitle : function(title){
35915     //    title = title || "&#160;";
35916      //   if(this.collapsedTitleTextEl){
35917       //      this.collapsedTitleTextEl.innerHTML = title;
35918        // }
35919     //},
35920
35921     getBox : function(){
35922         var b;
35923       //  if(!this.collapsed){
35924             b = this.el.getBox(false, true);
35925        // }else{
35926           //  b = this.collapsedEl.getBox(false, true);
35927         //}
35928         return b;
35929     },
35930
35931     getMargins : function(){
35932         return this.margins;
35933         //return this.collapsed ? this.cmargins : this.margins;
35934     },
35935 /*
35936     highlight : function(){
35937         this.el.addClass("x-layout-panel-dragover");
35938     },
35939
35940     unhighlight : function(){
35941         this.el.removeClass("x-layout-panel-dragover");
35942     },
35943 */
35944     updateBox : function(box)
35945     {
35946         if (!this.bodyEl) {
35947             return; // not rendered yet..
35948         }
35949         
35950         this.box = box;
35951         if(!this.collapsed){
35952             this.el.dom.style.left = box.x + "px";
35953             this.el.dom.style.top = box.y + "px";
35954             this.updateBody(box.width, box.height);
35955         }else{
35956             this.collapsedEl.dom.style.left = box.x + "px";
35957             this.collapsedEl.dom.style.top = box.y + "px";
35958             this.collapsedEl.setSize(box.width, box.height);
35959         }
35960         if(this.tabs){
35961             this.tabs.autoSizeTabs();
35962         }
35963     },
35964
35965     updateBody : function(w, h)
35966     {
35967         if(w !== null){
35968             this.el.setWidth(w);
35969             w -= this.el.getBorderWidth("rl");
35970             if(this.config.adjustments){
35971                 w += this.config.adjustments[0];
35972             }
35973         }
35974         if(h !== null && h > 0){
35975             this.el.setHeight(h);
35976             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35977             h -= this.el.getBorderWidth("tb");
35978             if(this.config.adjustments){
35979                 h += this.config.adjustments[1];
35980             }
35981             this.bodyEl.setHeight(h);
35982             if(this.tabs){
35983                 h = this.tabs.syncHeight(h);
35984             }
35985         }
35986         if(this.panelSize){
35987             w = w !== null ? w : this.panelSize.width;
35988             h = h !== null ? h : this.panelSize.height;
35989         }
35990         if(this.activePanel){
35991             var el = this.activePanel.getEl();
35992             w = w !== null ? w : el.getWidth();
35993             h = h !== null ? h : el.getHeight();
35994             this.panelSize = {width: w, height: h};
35995             this.activePanel.setSize(w, h);
35996         }
35997         if(Roo.isIE && this.tabs){
35998             this.tabs.el.repaint();
35999         }
36000     },
36001
36002     /**
36003      * Returns the container element for this region.
36004      * @return {Roo.Element}
36005      */
36006     getEl : function(){
36007         return this.el;
36008     },
36009
36010     /**
36011      * Hides this region.
36012      */
36013     hide : function(){
36014         //if(!this.collapsed){
36015             this.el.dom.style.left = "-2000px";
36016             this.el.hide();
36017         //}else{
36018          //   this.collapsedEl.dom.style.left = "-2000px";
36019          //   this.collapsedEl.hide();
36020        // }
36021         this.visible = false;
36022         this.fireEvent("visibilitychange", this, false);
36023     },
36024
36025     /**
36026      * Shows this region if it was previously hidden.
36027      */
36028     show : function(){
36029         //if(!this.collapsed){
36030             this.el.show();
36031         //}else{
36032         //    this.collapsedEl.show();
36033        // }
36034         this.visible = true;
36035         this.fireEvent("visibilitychange", this, true);
36036     },
36037 /*
36038     closeClicked : function(){
36039         if(this.activePanel){
36040             this.remove(this.activePanel);
36041         }
36042     },
36043
36044     collapseClick : function(e){
36045         if(this.isSlid){
36046            e.stopPropagation();
36047            this.slideIn();
36048         }else{
36049            e.stopPropagation();
36050            this.slideOut();
36051         }
36052     },
36053 */
36054     /**
36055      * Collapses this region.
36056      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36057      */
36058     /*
36059     collapse : function(skipAnim, skipCheck = false){
36060         if(this.collapsed) {
36061             return;
36062         }
36063         
36064         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36065             
36066             this.collapsed = true;
36067             if(this.split){
36068                 this.split.el.hide();
36069             }
36070             if(this.config.animate && skipAnim !== true){
36071                 this.fireEvent("invalidated", this);
36072                 this.animateCollapse();
36073             }else{
36074                 this.el.setLocation(-20000,-20000);
36075                 this.el.hide();
36076                 this.collapsedEl.show();
36077                 this.fireEvent("collapsed", this);
36078                 this.fireEvent("invalidated", this);
36079             }
36080         }
36081         
36082     },
36083 */
36084     animateCollapse : function(){
36085         // overridden
36086     },
36087
36088     /**
36089      * Expands this region if it was previously collapsed.
36090      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36091      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36092      */
36093     /*
36094     expand : function(e, skipAnim){
36095         if(e) {
36096             e.stopPropagation();
36097         }
36098         if(!this.collapsed || this.el.hasActiveFx()) {
36099             return;
36100         }
36101         if(this.isSlid){
36102             this.afterSlideIn();
36103             skipAnim = true;
36104         }
36105         this.collapsed = false;
36106         if(this.config.animate && skipAnim !== true){
36107             this.animateExpand();
36108         }else{
36109             this.el.show();
36110             if(this.split){
36111                 this.split.el.show();
36112             }
36113             this.collapsedEl.setLocation(-2000,-2000);
36114             this.collapsedEl.hide();
36115             this.fireEvent("invalidated", this);
36116             this.fireEvent("expanded", this);
36117         }
36118     },
36119 */
36120     animateExpand : function(){
36121         // overridden
36122     },
36123
36124     initTabs : function()
36125     {
36126         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36127         
36128         var ts = new Roo.bootstrap.panel.Tabs({
36129                 el: this.bodyEl.dom,
36130                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
36131                 disableTooltips: this.config.disableTabTips,
36132                 toolbar : this.config.toolbar
36133             });
36134         
36135         if(this.config.hideTabs){
36136             ts.stripWrap.setDisplayed(false);
36137         }
36138         this.tabs = ts;
36139         ts.resizeTabs = this.config.resizeTabs === true;
36140         ts.minTabWidth = this.config.minTabWidth || 40;
36141         ts.maxTabWidth = this.config.maxTabWidth || 250;
36142         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36143         ts.monitorResize = false;
36144         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36145         ts.bodyEl.addClass('roo-layout-tabs-body');
36146         this.panels.each(this.initPanelAsTab, this);
36147     },
36148
36149     initPanelAsTab : function(panel){
36150         var ti = this.tabs.addTab(
36151             panel.getEl().id,
36152             panel.getTitle(),
36153             null,
36154             this.config.closeOnTab && panel.isClosable(),
36155             panel.tpl
36156         );
36157         if(panel.tabTip !== undefined){
36158             ti.setTooltip(panel.tabTip);
36159         }
36160         ti.on("activate", function(){
36161               this.setActivePanel(panel);
36162         }, this);
36163         
36164         if(this.config.closeOnTab){
36165             ti.on("beforeclose", function(t, e){
36166                 e.cancel = true;
36167                 this.remove(panel);
36168             }, this);
36169         }
36170         
36171         panel.tabItem = ti;
36172         
36173         return ti;
36174     },
36175
36176     updatePanelTitle : function(panel, title)
36177     {
36178         if(this.activePanel == panel){
36179             this.updateTitle(title);
36180         }
36181         if(this.tabs){
36182             var ti = this.tabs.getTab(panel.getEl().id);
36183             ti.setText(title);
36184             if(panel.tabTip !== undefined){
36185                 ti.setTooltip(panel.tabTip);
36186             }
36187         }
36188     },
36189
36190     updateTitle : function(title){
36191         if(this.titleTextEl && !this.config.title){
36192             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36193         }
36194     },
36195
36196     setActivePanel : function(panel)
36197     {
36198         panel = this.getPanel(panel);
36199         if(this.activePanel && this.activePanel != panel){
36200             if(this.activePanel.setActiveState(false) === false){
36201                 return;
36202             }
36203         }
36204         this.activePanel = panel;
36205         panel.setActiveState(true);
36206         if(this.panelSize){
36207             panel.setSize(this.panelSize.width, this.panelSize.height);
36208         }
36209         if(this.closeBtn){
36210             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36211         }
36212         this.updateTitle(panel.getTitle());
36213         if(this.tabs){
36214             this.fireEvent("invalidated", this);
36215         }
36216         this.fireEvent("panelactivated", this, panel);
36217     },
36218
36219     /**
36220      * Shows the specified panel.
36221      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36222      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36223      */
36224     showPanel : function(panel)
36225     {
36226         panel = this.getPanel(panel);
36227         if(panel){
36228             if(this.tabs){
36229                 var tab = this.tabs.getTab(panel.getEl().id);
36230                 if(tab.isHidden()){
36231                     this.tabs.unhideTab(tab.id);
36232                 }
36233                 tab.activate();
36234             }else{
36235                 this.setActivePanel(panel);
36236             }
36237         }
36238         return panel;
36239     },
36240
36241     /**
36242      * Get the active panel for this region.
36243      * @return {Roo.ContentPanel} The active panel or null
36244      */
36245     getActivePanel : function(){
36246         return this.activePanel;
36247     },
36248
36249     validateVisibility : function(){
36250         if(this.panels.getCount() < 1){
36251             this.updateTitle("&#160;");
36252             this.closeBtn.hide();
36253             this.hide();
36254         }else{
36255             if(!this.isVisible()){
36256                 this.show();
36257             }
36258         }
36259     },
36260
36261     /**
36262      * Adds the passed ContentPanel(s) to this region.
36263      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36264      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36265      */
36266     add : function(panel)
36267     {
36268         if(arguments.length > 1){
36269             for(var i = 0, len = arguments.length; i < len; i++) {
36270                 this.add(arguments[i]);
36271             }
36272             return null;
36273         }
36274         
36275         // if we have not been rendered yet, then we can not really do much of this..
36276         if (!this.bodyEl) {
36277             this.unrendered_panels.push(panel);
36278             return panel;
36279         }
36280         
36281         
36282         
36283         
36284         if(this.hasPanel(panel)){
36285             this.showPanel(panel);
36286             return panel;
36287         }
36288         panel.setRegion(this);
36289         this.panels.add(panel);
36290        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36291             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36292             // and hide them... ???
36293             this.bodyEl.dom.appendChild(panel.getEl().dom);
36294             if(panel.background !== true){
36295                 this.setActivePanel(panel);
36296             }
36297             this.fireEvent("paneladded", this, panel);
36298             return panel;
36299         }
36300         */
36301         if(!this.tabs){
36302             this.initTabs();
36303         }else{
36304             this.initPanelAsTab(panel);
36305         }
36306         
36307         
36308         if(panel.background !== true){
36309             this.tabs.activate(panel.getEl().id);
36310         }
36311         this.fireEvent("paneladded", this, panel);
36312         return panel;
36313     },
36314
36315     /**
36316      * Hides the tab for the specified panel.
36317      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36318      */
36319     hidePanel : function(panel){
36320         if(this.tabs && (panel = this.getPanel(panel))){
36321             this.tabs.hideTab(panel.getEl().id);
36322         }
36323     },
36324
36325     /**
36326      * Unhides the tab for a previously hidden panel.
36327      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36328      */
36329     unhidePanel : function(panel){
36330         if(this.tabs && (panel = this.getPanel(panel))){
36331             this.tabs.unhideTab(panel.getEl().id);
36332         }
36333     },
36334
36335     clearPanels : function(){
36336         while(this.panels.getCount() > 0){
36337              this.remove(this.panels.first());
36338         }
36339     },
36340
36341     /**
36342      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36343      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36344      * @param {Boolean} preservePanel Overrides the config preservePanel option
36345      * @return {Roo.ContentPanel} The panel that was removed
36346      */
36347     remove : function(panel, preservePanel)
36348     {
36349         panel = this.getPanel(panel);
36350         if(!panel){
36351             return null;
36352         }
36353         var e = {};
36354         this.fireEvent("beforeremove", this, panel, e);
36355         if(e.cancel === true){
36356             return null;
36357         }
36358         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36359         var panelId = panel.getId();
36360         this.panels.removeKey(panelId);
36361         if(preservePanel){
36362             document.body.appendChild(panel.getEl().dom);
36363         }
36364         if(this.tabs){
36365             this.tabs.removeTab(panel.getEl().id);
36366         }else if (!preservePanel){
36367             this.bodyEl.dom.removeChild(panel.getEl().dom);
36368         }
36369         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36370             var p = this.panels.first();
36371             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36372             tempEl.appendChild(p.getEl().dom);
36373             this.bodyEl.update("");
36374             this.bodyEl.dom.appendChild(p.getEl().dom);
36375             tempEl = null;
36376             this.updateTitle(p.getTitle());
36377             this.tabs = null;
36378             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36379             this.setActivePanel(p);
36380         }
36381         panel.setRegion(null);
36382         if(this.activePanel == panel){
36383             this.activePanel = null;
36384         }
36385         if(this.config.autoDestroy !== false && preservePanel !== true){
36386             try{panel.destroy();}catch(e){}
36387         }
36388         this.fireEvent("panelremoved", this, panel);
36389         return panel;
36390     },
36391
36392     /**
36393      * Returns the TabPanel component used by this region
36394      * @return {Roo.TabPanel}
36395      */
36396     getTabs : function(){
36397         return this.tabs;
36398     },
36399
36400     createTool : function(parentEl, className){
36401         var btn = Roo.DomHelper.append(parentEl, {
36402             tag: "div",
36403             cls: "x-layout-tools-button",
36404             children: [ {
36405                 tag: "div",
36406                 cls: "roo-layout-tools-button-inner " + className,
36407                 html: "&#160;"
36408             }]
36409         }, true);
36410         btn.addClassOnOver("roo-layout-tools-button-over");
36411         return btn;
36412     }
36413 });/*
36414  * Based on:
36415  * Ext JS Library 1.1.1
36416  * Copyright(c) 2006-2007, Ext JS, LLC.
36417  *
36418  * Originally Released Under LGPL - original licence link has changed is not relivant.
36419  *
36420  * Fork - LGPL
36421  * <script type="text/javascript">
36422  */
36423  
36424
36425
36426 /**
36427  * @class Roo.SplitLayoutRegion
36428  * @extends Roo.LayoutRegion
36429  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36430  */
36431 Roo.bootstrap.layout.Split = function(config){
36432     this.cursor = config.cursor;
36433     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36434 };
36435
36436 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36437 {
36438     splitTip : "Drag to resize.",
36439     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36440     useSplitTips : false,
36441
36442     applyConfig : function(config){
36443         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36444     },
36445     
36446     onRender : function(ctr,pos) {
36447         
36448         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36449         if(!this.config.split){
36450             return;
36451         }
36452         if(!this.split){
36453             
36454             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36455                             tag: "div",
36456                             id: this.el.id + "-split",
36457                             cls: "roo-layout-split roo-layout-split-"+this.position,
36458                             html: "&#160;"
36459             });
36460             /** The SplitBar for this region 
36461             * @type Roo.SplitBar */
36462             // does not exist yet...
36463             Roo.log([this.position, this.orientation]);
36464             
36465             this.split = new Roo.bootstrap.SplitBar({
36466                 dragElement : splitEl,
36467                 resizingElement: this.el,
36468                 orientation : this.orientation
36469             });
36470             
36471             this.split.on("moved", this.onSplitMove, this);
36472             this.split.useShim = this.config.useShim === true;
36473             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36474             if(this.useSplitTips){
36475                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36476             }
36477             //if(config.collapsible){
36478             //    this.split.el.on("dblclick", this.collapse,  this);
36479             //}
36480         }
36481         if(typeof this.config.minSize != "undefined"){
36482             this.split.minSize = this.config.minSize;
36483         }
36484         if(typeof this.config.maxSize != "undefined"){
36485             this.split.maxSize = this.config.maxSize;
36486         }
36487         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36488             this.hideSplitter();
36489         }
36490         
36491     },
36492
36493     getHMaxSize : function(){
36494          var cmax = this.config.maxSize || 10000;
36495          var center = this.mgr.getRegion("center");
36496          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36497     },
36498
36499     getVMaxSize : function(){
36500          var cmax = this.config.maxSize || 10000;
36501          var center = this.mgr.getRegion("center");
36502          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36503     },
36504
36505     onSplitMove : function(split, newSize){
36506         this.fireEvent("resized", this, newSize);
36507     },
36508     
36509     /** 
36510      * Returns the {@link Roo.SplitBar} for this region.
36511      * @return {Roo.SplitBar}
36512      */
36513     getSplitBar : function(){
36514         return this.split;
36515     },
36516     
36517     hide : function(){
36518         this.hideSplitter();
36519         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36520     },
36521
36522     hideSplitter : function(){
36523         if(this.split){
36524             this.split.el.setLocation(-2000,-2000);
36525             this.split.el.hide();
36526         }
36527     },
36528
36529     show : function(){
36530         if(this.split){
36531             this.split.el.show();
36532         }
36533         Roo.bootstrap.layout.Split.superclass.show.call(this);
36534     },
36535     
36536     beforeSlide: function(){
36537         if(Roo.isGecko){// firefox overflow auto bug workaround
36538             this.bodyEl.clip();
36539             if(this.tabs) {
36540                 this.tabs.bodyEl.clip();
36541             }
36542             if(this.activePanel){
36543                 this.activePanel.getEl().clip();
36544                 
36545                 if(this.activePanel.beforeSlide){
36546                     this.activePanel.beforeSlide();
36547                 }
36548             }
36549         }
36550     },
36551     
36552     afterSlide : function(){
36553         if(Roo.isGecko){// firefox overflow auto bug workaround
36554             this.bodyEl.unclip();
36555             if(this.tabs) {
36556                 this.tabs.bodyEl.unclip();
36557             }
36558             if(this.activePanel){
36559                 this.activePanel.getEl().unclip();
36560                 if(this.activePanel.afterSlide){
36561                     this.activePanel.afterSlide();
36562                 }
36563             }
36564         }
36565     },
36566
36567     initAutoHide : function(){
36568         if(this.autoHide !== false){
36569             if(!this.autoHideHd){
36570                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36571                 this.autoHideHd = {
36572                     "mouseout": function(e){
36573                         if(!e.within(this.el, true)){
36574                             st.delay(500);
36575                         }
36576                     },
36577                     "mouseover" : function(e){
36578                         st.cancel();
36579                     },
36580                     scope : this
36581                 };
36582             }
36583             this.el.on(this.autoHideHd);
36584         }
36585     },
36586
36587     clearAutoHide : function(){
36588         if(this.autoHide !== false){
36589             this.el.un("mouseout", this.autoHideHd.mouseout);
36590             this.el.un("mouseover", this.autoHideHd.mouseover);
36591         }
36592     },
36593
36594     clearMonitor : function(){
36595         Roo.get(document).un("click", this.slideInIf, this);
36596     },
36597
36598     // these names are backwards but not changed for compat
36599     slideOut : function(){
36600         if(this.isSlid || this.el.hasActiveFx()){
36601             return;
36602         }
36603         this.isSlid = true;
36604         if(this.collapseBtn){
36605             this.collapseBtn.hide();
36606         }
36607         this.closeBtnState = this.closeBtn.getStyle('display');
36608         this.closeBtn.hide();
36609         if(this.stickBtn){
36610             this.stickBtn.show();
36611         }
36612         this.el.show();
36613         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36614         this.beforeSlide();
36615         this.el.setStyle("z-index", 10001);
36616         this.el.slideIn(this.getSlideAnchor(), {
36617             callback: function(){
36618                 this.afterSlide();
36619                 this.initAutoHide();
36620                 Roo.get(document).on("click", this.slideInIf, this);
36621                 this.fireEvent("slideshow", this);
36622             },
36623             scope: this,
36624             block: true
36625         });
36626     },
36627
36628     afterSlideIn : function(){
36629         this.clearAutoHide();
36630         this.isSlid = false;
36631         this.clearMonitor();
36632         this.el.setStyle("z-index", "");
36633         if(this.collapseBtn){
36634             this.collapseBtn.show();
36635         }
36636         this.closeBtn.setStyle('display', this.closeBtnState);
36637         if(this.stickBtn){
36638             this.stickBtn.hide();
36639         }
36640         this.fireEvent("slidehide", this);
36641     },
36642
36643     slideIn : function(cb){
36644         if(!this.isSlid || this.el.hasActiveFx()){
36645             Roo.callback(cb);
36646             return;
36647         }
36648         this.isSlid = false;
36649         this.beforeSlide();
36650         this.el.slideOut(this.getSlideAnchor(), {
36651             callback: function(){
36652                 this.el.setLeftTop(-10000, -10000);
36653                 this.afterSlide();
36654                 this.afterSlideIn();
36655                 Roo.callback(cb);
36656             },
36657             scope: this,
36658             block: true
36659         });
36660     },
36661     
36662     slideInIf : function(e){
36663         if(!e.within(this.el)){
36664             this.slideIn();
36665         }
36666     },
36667
36668     animateCollapse : function(){
36669         this.beforeSlide();
36670         this.el.setStyle("z-index", 20000);
36671         var anchor = this.getSlideAnchor();
36672         this.el.slideOut(anchor, {
36673             callback : function(){
36674                 this.el.setStyle("z-index", "");
36675                 this.collapsedEl.slideIn(anchor, {duration:.3});
36676                 this.afterSlide();
36677                 this.el.setLocation(-10000,-10000);
36678                 this.el.hide();
36679                 this.fireEvent("collapsed", this);
36680             },
36681             scope: this,
36682             block: true
36683         });
36684     },
36685
36686     animateExpand : function(){
36687         this.beforeSlide();
36688         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36689         this.el.setStyle("z-index", 20000);
36690         this.collapsedEl.hide({
36691             duration:.1
36692         });
36693         this.el.slideIn(this.getSlideAnchor(), {
36694             callback : function(){
36695                 this.el.setStyle("z-index", "");
36696                 this.afterSlide();
36697                 if(this.split){
36698                     this.split.el.show();
36699                 }
36700                 this.fireEvent("invalidated", this);
36701                 this.fireEvent("expanded", this);
36702             },
36703             scope: this,
36704             block: true
36705         });
36706     },
36707
36708     anchors : {
36709         "west" : "left",
36710         "east" : "right",
36711         "north" : "top",
36712         "south" : "bottom"
36713     },
36714
36715     sanchors : {
36716         "west" : "l",
36717         "east" : "r",
36718         "north" : "t",
36719         "south" : "b"
36720     },
36721
36722     canchors : {
36723         "west" : "tl-tr",
36724         "east" : "tr-tl",
36725         "north" : "tl-bl",
36726         "south" : "bl-tl"
36727     },
36728
36729     getAnchor : function(){
36730         return this.anchors[this.position];
36731     },
36732
36733     getCollapseAnchor : function(){
36734         return this.canchors[this.position];
36735     },
36736
36737     getSlideAnchor : function(){
36738         return this.sanchors[this.position];
36739     },
36740
36741     getAlignAdj : function(){
36742         var cm = this.cmargins;
36743         switch(this.position){
36744             case "west":
36745                 return [0, 0];
36746             break;
36747             case "east":
36748                 return [0, 0];
36749             break;
36750             case "north":
36751                 return [0, 0];
36752             break;
36753             case "south":
36754                 return [0, 0];
36755             break;
36756         }
36757     },
36758
36759     getExpandAdj : function(){
36760         var c = this.collapsedEl, cm = this.cmargins;
36761         switch(this.position){
36762             case "west":
36763                 return [-(cm.right+c.getWidth()+cm.left), 0];
36764             break;
36765             case "east":
36766                 return [cm.right+c.getWidth()+cm.left, 0];
36767             break;
36768             case "north":
36769                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36770             break;
36771             case "south":
36772                 return [0, cm.top+cm.bottom+c.getHeight()];
36773             break;
36774         }
36775     }
36776 });/*
36777  * Based on:
36778  * Ext JS Library 1.1.1
36779  * Copyright(c) 2006-2007, Ext JS, LLC.
36780  *
36781  * Originally Released Under LGPL - original licence link has changed is not relivant.
36782  *
36783  * Fork - LGPL
36784  * <script type="text/javascript">
36785  */
36786 /*
36787  * These classes are private internal classes
36788  */
36789 Roo.bootstrap.layout.Center = function(config){
36790     config.region = "center";
36791     Roo.bootstrap.layout.Region.call(this, config);
36792     this.visible = true;
36793     this.minWidth = config.minWidth || 20;
36794     this.minHeight = config.minHeight || 20;
36795 };
36796
36797 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36798     hide : function(){
36799         // center panel can't be hidden
36800     },
36801     
36802     show : function(){
36803         // center panel can't be hidden
36804     },
36805     
36806     getMinWidth: function(){
36807         return this.minWidth;
36808     },
36809     
36810     getMinHeight: function(){
36811         return this.minHeight;
36812     }
36813 });
36814
36815
36816
36817
36818  
36819
36820
36821
36822
36823
36824 Roo.bootstrap.layout.North = function(config)
36825 {
36826     config.region = 'north';
36827     config.cursor = 'n-resize';
36828     
36829     Roo.bootstrap.layout.Split.call(this, config);
36830     
36831     
36832     if(this.split){
36833         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36834         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36835         this.split.el.addClass("roo-layout-split-v");
36836     }
36837     var size = config.initialSize || config.height;
36838     if(typeof size != "undefined"){
36839         this.el.setHeight(size);
36840     }
36841 };
36842 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36843 {
36844     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36845     
36846     
36847     
36848     getBox : function(){
36849         if(this.collapsed){
36850             return this.collapsedEl.getBox();
36851         }
36852         var box = this.el.getBox();
36853         if(this.split){
36854             box.height += this.split.el.getHeight();
36855         }
36856         return box;
36857     },
36858     
36859     updateBox : function(box){
36860         if(this.split && !this.collapsed){
36861             box.height -= this.split.el.getHeight();
36862             this.split.el.setLeft(box.x);
36863             this.split.el.setTop(box.y+box.height);
36864             this.split.el.setWidth(box.width);
36865         }
36866         if(this.collapsed){
36867             this.updateBody(box.width, null);
36868         }
36869         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36870     }
36871 });
36872
36873
36874
36875
36876
36877 Roo.bootstrap.layout.South = function(config){
36878     config.region = 'south';
36879     config.cursor = 's-resize';
36880     Roo.bootstrap.layout.Split.call(this, config);
36881     if(this.split){
36882         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36883         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36884         this.split.el.addClass("roo-layout-split-v");
36885     }
36886     var size = config.initialSize || config.height;
36887     if(typeof size != "undefined"){
36888         this.el.setHeight(size);
36889     }
36890 };
36891
36892 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36893     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36894     getBox : function(){
36895         if(this.collapsed){
36896             return this.collapsedEl.getBox();
36897         }
36898         var box = this.el.getBox();
36899         if(this.split){
36900             var sh = this.split.el.getHeight();
36901             box.height += sh;
36902             box.y -= sh;
36903         }
36904         return box;
36905     },
36906     
36907     updateBox : function(box){
36908         if(this.split && !this.collapsed){
36909             var sh = this.split.el.getHeight();
36910             box.height -= sh;
36911             box.y += sh;
36912             this.split.el.setLeft(box.x);
36913             this.split.el.setTop(box.y-sh);
36914             this.split.el.setWidth(box.width);
36915         }
36916         if(this.collapsed){
36917             this.updateBody(box.width, null);
36918         }
36919         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36920     }
36921 });
36922
36923 Roo.bootstrap.layout.East = function(config){
36924     config.region = "east";
36925     config.cursor = "e-resize";
36926     Roo.bootstrap.layout.Split.call(this, config);
36927     if(this.split){
36928         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36929         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36930         this.split.el.addClass("roo-layout-split-h");
36931     }
36932     var size = config.initialSize || config.width;
36933     if(typeof size != "undefined"){
36934         this.el.setWidth(size);
36935     }
36936 };
36937 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36938     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36939     getBox : function(){
36940         if(this.collapsed){
36941             return this.collapsedEl.getBox();
36942         }
36943         var box = this.el.getBox();
36944         if(this.split){
36945             var sw = this.split.el.getWidth();
36946             box.width += sw;
36947             box.x -= sw;
36948         }
36949         return box;
36950     },
36951
36952     updateBox : function(box){
36953         if(this.split && !this.collapsed){
36954             var sw = this.split.el.getWidth();
36955             box.width -= sw;
36956             this.split.el.setLeft(box.x);
36957             this.split.el.setTop(box.y);
36958             this.split.el.setHeight(box.height);
36959             box.x += sw;
36960         }
36961         if(this.collapsed){
36962             this.updateBody(null, box.height);
36963         }
36964         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36965     }
36966 });
36967
36968 Roo.bootstrap.layout.West = function(config){
36969     config.region = "west";
36970     config.cursor = "w-resize";
36971     
36972     Roo.bootstrap.layout.Split.call(this, config);
36973     if(this.split){
36974         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36975         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36976         this.split.el.addClass("roo-layout-split-h");
36977     }
36978     
36979 };
36980 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36981     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36982     
36983     onRender: function(ctr, pos)
36984     {
36985         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36986         var size = this.config.initialSize || this.config.width;
36987         if(typeof size != "undefined"){
36988             this.el.setWidth(size);
36989         }
36990     },
36991     
36992     getBox : function(){
36993         if(this.collapsed){
36994             return this.collapsedEl.getBox();
36995         }
36996         var box = this.el.getBox();
36997         if(this.split){
36998             box.width += this.split.el.getWidth();
36999         }
37000         return box;
37001     },
37002     
37003     updateBox : function(box){
37004         if(this.split && !this.collapsed){
37005             var sw = this.split.el.getWidth();
37006             box.width -= sw;
37007             this.split.el.setLeft(box.x+box.width);
37008             this.split.el.setTop(box.y);
37009             this.split.el.setHeight(box.height);
37010         }
37011         if(this.collapsed){
37012             this.updateBody(null, box.height);
37013         }
37014         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37015     }
37016 });
37017 Roo.namespace("Roo.bootstrap.panel");/*
37018  * Based on:
37019  * Ext JS Library 1.1.1
37020  * Copyright(c) 2006-2007, Ext JS, LLC.
37021  *
37022  * Originally Released Under LGPL - original licence link has changed is not relivant.
37023  *
37024  * Fork - LGPL
37025  * <script type="text/javascript">
37026  */
37027 /**
37028  * @class Roo.ContentPanel
37029  * @extends Roo.util.Observable
37030  * A basic ContentPanel element.
37031  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37032  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37033  * @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
37034  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37035  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37036  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37037  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37038  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37039  * @cfg {String} title          The title for this panel
37040  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37041  * @cfg {String} url            Calls {@link #setUrl} with this value
37042  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37043  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37044  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37045  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37046  * @cfg {Boolean} badges render the badges
37047
37048  * @constructor
37049  * Create a new ContentPanel.
37050  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37051  * @param {String/Object} config A string to set only the title or a config object
37052  * @param {String} content (optional) Set the HTML content for this panel
37053  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37054  */
37055 Roo.bootstrap.panel.Content = function( config){
37056     
37057     this.tpl = config.tpl || false;
37058     
37059     var el = config.el;
37060     var content = config.content;
37061
37062     if(config.autoCreate){ // xtype is available if this is called from factory
37063         el = Roo.id();
37064     }
37065     this.el = Roo.get(el);
37066     if(!this.el && config && config.autoCreate){
37067         if(typeof config.autoCreate == "object"){
37068             if(!config.autoCreate.id){
37069                 config.autoCreate.id = config.id||el;
37070             }
37071             this.el = Roo.DomHelper.append(document.body,
37072                         config.autoCreate, true);
37073         }else{
37074             var elcfg =  {   tag: "div",
37075                             cls: "roo-layout-inactive-content",
37076                             id: config.id||el
37077                             };
37078             if (config.html) {
37079                 elcfg.html = config.html;
37080                 
37081             }
37082                         
37083             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37084         }
37085     } 
37086     this.closable = false;
37087     this.loaded = false;
37088     this.active = false;
37089    
37090       
37091     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37092         
37093         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37094         
37095         this.wrapEl = this.el; //this.el.wrap();
37096         var ti = [];
37097         if (config.toolbar.items) {
37098             ti = config.toolbar.items ;
37099             delete config.toolbar.items ;
37100         }
37101         
37102         var nitems = [];
37103         this.toolbar.render(this.wrapEl, 'before');
37104         for(var i =0;i < ti.length;i++) {
37105           //  Roo.log(['add child', items[i]]);
37106             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37107         }
37108         this.toolbar.items = nitems;
37109         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37110         delete config.toolbar;
37111         
37112     }
37113     /*
37114     // xtype created footer. - not sure if will work as we normally have to render first..
37115     if (this.footer && !this.footer.el && this.footer.xtype) {
37116         if (!this.wrapEl) {
37117             this.wrapEl = this.el.wrap();
37118         }
37119     
37120         this.footer.container = this.wrapEl.createChild();
37121          
37122         this.footer = Roo.factory(this.footer, Roo);
37123         
37124     }
37125     */
37126     
37127      if(typeof config == "string"){
37128         this.title = config;
37129     }else{
37130         Roo.apply(this, config);
37131     }
37132     
37133     if(this.resizeEl){
37134         this.resizeEl = Roo.get(this.resizeEl, true);
37135     }else{
37136         this.resizeEl = this.el;
37137     }
37138     // handle view.xtype
37139     
37140  
37141     
37142     
37143     this.addEvents({
37144         /**
37145          * @event activate
37146          * Fires when this panel is activated. 
37147          * @param {Roo.ContentPanel} this
37148          */
37149         "activate" : true,
37150         /**
37151          * @event deactivate
37152          * Fires when this panel is activated. 
37153          * @param {Roo.ContentPanel} this
37154          */
37155         "deactivate" : true,
37156
37157         /**
37158          * @event resize
37159          * Fires when this panel is resized if fitToFrame is true.
37160          * @param {Roo.ContentPanel} this
37161          * @param {Number} width The width after any component adjustments
37162          * @param {Number} height The height after any component adjustments
37163          */
37164         "resize" : true,
37165         
37166          /**
37167          * @event render
37168          * Fires when this tab is created
37169          * @param {Roo.ContentPanel} this
37170          */
37171         "render" : true
37172         
37173         
37174         
37175     });
37176     
37177
37178     
37179     
37180     if(this.autoScroll){
37181         this.resizeEl.setStyle("overflow", "auto");
37182     } else {
37183         // fix randome scrolling
37184         //this.el.on('scroll', function() {
37185         //    Roo.log('fix random scolling');
37186         //    this.scrollTo('top',0); 
37187         //});
37188     }
37189     content = content || this.content;
37190     if(content){
37191         this.setContent(content);
37192     }
37193     if(config && config.url){
37194         this.setUrl(this.url, this.params, this.loadOnce);
37195     }
37196     
37197     
37198     
37199     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37200     
37201     if (this.view && typeof(this.view.xtype) != 'undefined') {
37202         this.view.el = this.el.appendChild(document.createElement("div"));
37203         this.view = Roo.factory(this.view); 
37204         this.view.render  &&  this.view.render(false, '');  
37205     }
37206     
37207     
37208     this.fireEvent('render', this);
37209 };
37210
37211 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37212     
37213     tabTip : '',
37214     
37215     setRegion : function(region){
37216         this.region = region;
37217         this.setActiveClass(region && !this.background);
37218     },
37219     
37220     
37221     setActiveClass: function(state)
37222     {
37223         if(state){
37224            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37225            this.el.setStyle('position','relative');
37226         }else{
37227            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37228            this.el.setStyle('position', 'absolute');
37229         } 
37230     },
37231     
37232     /**
37233      * Returns the toolbar for this Panel if one was configured. 
37234      * @return {Roo.Toolbar} 
37235      */
37236     getToolbar : function(){
37237         return this.toolbar;
37238     },
37239     
37240     setActiveState : function(active)
37241     {
37242         this.active = active;
37243         this.setActiveClass(active);
37244         if(!active){
37245             if(this.fireEvent("deactivate", this) === false){
37246                 return false;
37247             }
37248             return true;
37249         }
37250         this.fireEvent("activate", this);
37251         return true;
37252     },
37253     /**
37254      * Updates this panel's element
37255      * @param {String} content The new content
37256      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37257     */
37258     setContent : function(content, loadScripts){
37259         this.el.update(content, loadScripts);
37260     },
37261
37262     ignoreResize : function(w, h){
37263         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37264             return true;
37265         }else{
37266             this.lastSize = {width: w, height: h};
37267             return false;
37268         }
37269     },
37270     /**
37271      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37272      * @return {Roo.UpdateManager} The UpdateManager
37273      */
37274     getUpdateManager : function(){
37275         return this.el.getUpdateManager();
37276     },
37277      /**
37278      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37279      * @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:
37280 <pre><code>
37281 panel.load({
37282     url: "your-url.php",
37283     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37284     callback: yourFunction,
37285     scope: yourObject, //(optional scope)
37286     discardUrl: false,
37287     nocache: false,
37288     text: "Loading...",
37289     timeout: 30,
37290     scripts: false
37291 });
37292 </code></pre>
37293      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37294      * 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.
37295      * @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}
37296      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37297      * @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.
37298      * @return {Roo.ContentPanel} this
37299      */
37300     load : function(){
37301         var um = this.el.getUpdateManager();
37302         um.update.apply(um, arguments);
37303         return this;
37304     },
37305
37306
37307     /**
37308      * 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.
37309      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37310      * @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)
37311      * @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)
37312      * @return {Roo.UpdateManager} The UpdateManager
37313      */
37314     setUrl : function(url, params, loadOnce){
37315         if(this.refreshDelegate){
37316             this.removeListener("activate", this.refreshDelegate);
37317         }
37318         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37319         this.on("activate", this.refreshDelegate);
37320         return this.el.getUpdateManager();
37321     },
37322     
37323     _handleRefresh : function(url, params, loadOnce){
37324         if(!loadOnce || !this.loaded){
37325             var updater = this.el.getUpdateManager();
37326             updater.update(url, params, this._setLoaded.createDelegate(this));
37327         }
37328     },
37329     
37330     _setLoaded : function(){
37331         this.loaded = true;
37332     }, 
37333     
37334     /**
37335      * Returns this panel's id
37336      * @return {String} 
37337      */
37338     getId : function(){
37339         return this.el.id;
37340     },
37341     
37342     /** 
37343      * Returns this panel's element - used by regiosn to add.
37344      * @return {Roo.Element} 
37345      */
37346     getEl : function(){
37347         return this.wrapEl || this.el;
37348     },
37349     
37350    
37351     
37352     adjustForComponents : function(width, height)
37353     {
37354         //Roo.log('adjustForComponents ');
37355         if(this.resizeEl != this.el){
37356             width -= this.el.getFrameWidth('lr');
37357             height -= this.el.getFrameWidth('tb');
37358         }
37359         if(this.toolbar){
37360             var te = this.toolbar.getEl();
37361             te.setWidth(width);
37362             height -= te.getHeight();
37363         }
37364         if(this.footer){
37365             var te = this.footer.getEl();
37366             te.setWidth(width);
37367             height -= te.getHeight();
37368         }
37369         
37370         
37371         if(this.adjustments){
37372             width += this.adjustments[0];
37373             height += this.adjustments[1];
37374         }
37375         return {"width": width, "height": height};
37376     },
37377     
37378     setSize : function(width, height){
37379         if(this.fitToFrame && !this.ignoreResize(width, height)){
37380             if(this.fitContainer && this.resizeEl != this.el){
37381                 this.el.setSize(width, height);
37382             }
37383             var size = this.adjustForComponents(width, height);
37384             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37385             this.fireEvent('resize', this, size.width, size.height);
37386         }
37387     },
37388     
37389     /**
37390      * Returns this panel's title
37391      * @return {String} 
37392      */
37393     getTitle : function(){
37394         
37395         if (typeof(this.title) != 'object') {
37396             return this.title;
37397         }
37398         
37399         var t = '';
37400         for (var k in this.title) {
37401             if (!this.title.hasOwnProperty(k)) {
37402                 continue;
37403             }
37404             
37405             if (k.indexOf('-') >= 0) {
37406                 var s = k.split('-');
37407                 for (var i = 0; i<s.length; i++) {
37408                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37409                 }
37410             } else {
37411                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37412             }
37413         }
37414         return t;
37415     },
37416     
37417     /**
37418      * Set this panel's title
37419      * @param {String} title
37420      */
37421     setTitle : function(title){
37422         this.title = title;
37423         if(this.region){
37424             this.region.updatePanelTitle(this, title);
37425         }
37426     },
37427     
37428     /**
37429      * Returns true is this panel was configured to be closable
37430      * @return {Boolean} 
37431      */
37432     isClosable : function(){
37433         return this.closable;
37434     },
37435     
37436     beforeSlide : function(){
37437         this.el.clip();
37438         this.resizeEl.clip();
37439     },
37440     
37441     afterSlide : function(){
37442         this.el.unclip();
37443         this.resizeEl.unclip();
37444     },
37445     
37446     /**
37447      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37448      *   Will fail silently if the {@link #setUrl} method has not been called.
37449      *   This does not activate the panel, just updates its content.
37450      */
37451     refresh : function(){
37452         if(this.refreshDelegate){
37453            this.loaded = false;
37454            this.refreshDelegate();
37455         }
37456     },
37457     
37458     /**
37459      * Destroys this panel
37460      */
37461     destroy : function(){
37462         this.el.removeAllListeners();
37463         var tempEl = document.createElement("span");
37464         tempEl.appendChild(this.el.dom);
37465         tempEl.innerHTML = "";
37466         this.el.remove();
37467         this.el = null;
37468     },
37469     
37470     /**
37471      * form - if the content panel contains a form - this is a reference to it.
37472      * @type {Roo.form.Form}
37473      */
37474     form : false,
37475     /**
37476      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37477      *    This contains a reference to it.
37478      * @type {Roo.View}
37479      */
37480     view : false,
37481     
37482       /**
37483      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37484      * <pre><code>
37485
37486 layout.addxtype({
37487        xtype : 'Form',
37488        items: [ .... ]
37489    }
37490 );
37491
37492 </code></pre>
37493      * @param {Object} cfg Xtype definition of item to add.
37494      */
37495     
37496     
37497     getChildContainer: function () {
37498         return this.getEl();
37499     }
37500     
37501     
37502     /*
37503         var  ret = new Roo.factory(cfg);
37504         return ret;
37505         
37506         
37507         // add form..
37508         if (cfg.xtype.match(/^Form$/)) {
37509             
37510             var el;
37511             //if (this.footer) {
37512             //    el = this.footer.container.insertSibling(false, 'before');
37513             //} else {
37514                 el = this.el.createChild();
37515             //}
37516
37517             this.form = new  Roo.form.Form(cfg);
37518             
37519             
37520             if ( this.form.allItems.length) {
37521                 this.form.render(el.dom);
37522             }
37523             return this.form;
37524         }
37525         // should only have one of theses..
37526         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37527             // views.. should not be just added - used named prop 'view''
37528             
37529             cfg.el = this.el.appendChild(document.createElement("div"));
37530             // factory?
37531             
37532             var ret = new Roo.factory(cfg);
37533              
37534              ret.render && ret.render(false, ''); // render blank..
37535             this.view = ret;
37536             return ret;
37537         }
37538         return false;
37539     }
37540     \*/
37541 });
37542  
37543 /**
37544  * @class Roo.bootstrap.panel.Grid
37545  * @extends Roo.bootstrap.panel.Content
37546  * @constructor
37547  * Create a new GridPanel.
37548  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37549  * @param {Object} config A the config object
37550   
37551  */
37552
37553
37554
37555 Roo.bootstrap.panel.Grid = function(config)
37556 {
37557     
37558       
37559     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37560         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37561
37562     config.el = this.wrapper;
37563     //this.el = this.wrapper;
37564     
37565       if (config.container) {
37566         // ctor'ed from a Border/panel.grid
37567         
37568         
37569         this.wrapper.setStyle("overflow", "hidden");
37570         this.wrapper.addClass('roo-grid-container');
37571
37572     }
37573     
37574     
37575     if(config.toolbar){
37576         var tool_el = this.wrapper.createChild();    
37577         this.toolbar = Roo.factory(config.toolbar);
37578         var ti = [];
37579         if (config.toolbar.items) {
37580             ti = config.toolbar.items ;
37581             delete config.toolbar.items ;
37582         }
37583         
37584         var nitems = [];
37585         this.toolbar.render(tool_el);
37586         for(var i =0;i < ti.length;i++) {
37587           //  Roo.log(['add child', items[i]]);
37588             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37589         }
37590         this.toolbar.items = nitems;
37591         
37592         delete config.toolbar;
37593     }
37594     
37595     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37596     config.grid.scrollBody = true;;
37597     config.grid.monitorWindowResize = false; // turn off autosizing
37598     config.grid.autoHeight = false;
37599     config.grid.autoWidth = false;
37600     
37601     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37602     
37603     if (config.background) {
37604         // render grid on panel activation (if panel background)
37605         this.on('activate', function(gp) {
37606             if (!gp.grid.rendered) {
37607                 gp.grid.render(this.wrapper);
37608                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37609             }
37610         });
37611             
37612     } else {
37613         this.grid.render(this.wrapper);
37614         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37615
37616     }
37617     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37618     // ??? needed ??? config.el = this.wrapper;
37619     
37620     
37621     
37622   
37623     // xtype created footer. - not sure if will work as we normally have to render first..
37624     if (this.footer && !this.footer.el && this.footer.xtype) {
37625         
37626         var ctr = this.grid.getView().getFooterPanel(true);
37627         this.footer.dataSource = this.grid.dataSource;
37628         this.footer = Roo.factory(this.footer, Roo);
37629         this.footer.render(ctr);
37630         
37631     }
37632     
37633     
37634     
37635     
37636      
37637 };
37638
37639 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37640     getId : function(){
37641         return this.grid.id;
37642     },
37643     
37644     /**
37645      * Returns the grid for this panel
37646      * @return {Roo.bootstrap.Table} 
37647      */
37648     getGrid : function(){
37649         return this.grid;    
37650     },
37651     
37652     setSize : function(width, height){
37653         if(!this.ignoreResize(width, height)){
37654             var grid = this.grid;
37655             var size = this.adjustForComponents(width, height);
37656             var gridel = grid.getGridEl();
37657             gridel.setSize(size.width, size.height);
37658             /*
37659             var thd = grid.getGridEl().select('thead',true).first();
37660             var tbd = grid.getGridEl().select('tbody', true).first();
37661             if (tbd) {
37662                 tbd.setSize(width, height - thd.getHeight());
37663             }
37664             */
37665             grid.autoSize();
37666         }
37667     },
37668      
37669     
37670     
37671     beforeSlide : function(){
37672         this.grid.getView().scroller.clip();
37673     },
37674     
37675     afterSlide : function(){
37676         this.grid.getView().scroller.unclip();
37677     },
37678     
37679     destroy : function(){
37680         this.grid.destroy();
37681         delete this.grid;
37682         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37683     }
37684 });
37685
37686 /**
37687  * @class Roo.bootstrap.panel.Nest
37688  * @extends Roo.bootstrap.panel.Content
37689  * @constructor
37690  * Create a new Panel, that can contain a layout.Border.
37691  * 
37692  * 
37693  * @param {Roo.BorderLayout} layout The layout for this panel
37694  * @param {String/Object} config A string to set only the title or a config object
37695  */
37696 Roo.bootstrap.panel.Nest = function(config)
37697 {
37698     // construct with only one argument..
37699     /* FIXME - implement nicer consturctors
37700     if (layout.layout) {
37701         config = layout;
37702         layout = config.layout;
37703         delete config.layout;
37704     }
37705     if (layout.xtype && !layout.getEl) {
37706         // then layout needs constructing..
37707         layout = Roo.factory(layout, Roo);
37708     }
37709     */
37710     
37711     config.el =  config.layout.getEl();
37712     
37713     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37714     
37715     config.layout.monitorWindowResize = false; // turn off autosizing
37716     this.layout = config.layout;
37717     this.layout.getEl().addClass("roo-layout-nested-layout");
37718     
37719     
37720     
37721     
37722 };
37723
37724 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37725
37726     setSize : function(width, height){
37727         if(!this.ignoreResize(width, height)){
37728             var size = this.adjustForComponents(width, height);
37729             var el = this.layout.getEl();
37730             if (size.height < 1) {
37731                 el.setWidth(size.width);   
37732             } else {
37733                 el.setSize(size.width, size.height);
37734             }
37735             var touch = el.dom.offsetWidth;
37736             this.layout.layout();
37737             // ie requires a double layout on the first pass
37738             if(Roo.isIE && !this.initialized){
37739                 this.initialized = true;
37740                 this.layout.layout();
37741             }
37742         }
37743     },
37744     
37745     // activate all subpanels if not currently active..
37746     
37747     setActiveState : function(active){
37748         this.active = active;
37749         this.setActiveClass(active);
37750         
37751         if(!active){
37752             this.fireEvent("deactivate", this);
37753             return;
37754         }
37755         
37756         this.fireEvent("activate", this);
37757         // not sure if this should happen before or after..
37758         if (!this.layout) {
37759             return; // should not happen..
37760         }
37761         var reg = false;
37762         for (var r in this.layout.regions) {
37763             reg = this.layout.getRegion(r);
37764             if (reg.getActivePanel()) {
37765                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37766                 reg.setActivePanel(reg.getActivePanel());
37767                 continue;
37768             }
37769             if (!reg.panels.length) {
37770                 continue;
37771             }
37772             reg.showPanel(reg.getPanel(0));
37773         }
37774         
37775         
37776         
37777         
37778     },
37779     
37780     /**
37781      * Returns the nested BorderLayout for this panel
37782      * @return {Roo.BorderLayout} 
37783      */
37784     getLayout : function(){
37785         return this.layout;
37786     },
37787     
37788      /**
37789      * Adds a xtype elements to the layout of the nested panel
37790      * <pre><code>
37791
37792 panel.addxtype({
37793        xtype : 'ContentPanel',
37794        region: 'west',
37795        items: [ .... ]
37796    }
37797 );
37798
37799 panel.addxtype({
37800         xtype : 'NestedLayoutPanel',
37801         region: 'west',
37802         layout: {
37803            center: { },
37804            west: { }   
37805         },
37806         items : [ ... list of content panels or nested layout panels.. ]
37807    }
37808 );
37809 </code></pre>
37810      * @param {Object} cfg Xtype definition of item to add.
37811      */
37812     addxtype : function(cfg) {
37813         return this.layout.addxtype(cfg);
37814     
37815     }
37816 });        /*
37817  * Based on:
37818  * Ext JS Library 1.1.1
37819  * Copyright(c) 2006-2007, Ext JS, LLC.
37820  *
37821  * Originally Released Under LGPL - original licence link has changed is not relivant.
37822  *
37823  * Fork - LGPL
37824  * <script type="text/javascript">
37825  */
37826 /**
37827  * @class Roo.TabPanel
37828  * @extends Roo.util.Observable
37829  * A lightweight tab container.
37830  * <br><br>
37831  * Usage:
37832  * <pre><code>
37833 // basic tabs 1, built from existing content
37834 var tabs = new Roo.TabPanel("tabs1");
37835 tabs.addTab("script", "View Script");
37836 tabs.addTab("markup", "View Markup");
37837 tabs.activate("script");
37838
37839 // more advanced tabs, built from javascript
37840 var jtabs = new Roo.TabPanel("jtabs");
37841 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37842
37843 // set up the UpdateManager
37844 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37845 var updater = tab2.getUpdateManager();
37846 updater.setDefaultUrl("ajax1.htm");
37847 tab2.on('activate', updater.refresh, updater, true);
37848
37849 // Use setUrl for Ajax loading
37850 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37851 tab3.setUrl("ajax2.htm", null, true);
37852
37853 // Disabled tab
37854 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37855 tab4.disable();
37856
37857 jtabs.activate("jtabs-1");
37858  * </code></pre>
37859  * @constructor
37860  * Create a new TabPanel.
37861  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37862  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37863  */
37864 Roo.bootstrap.panel.Tabs = function(config){
37865     /**
37866     * The container element for this TabPanel.
37867     * @type Roo.Element
37868     */
37869     this.el = Roo.get(config.el);
37870     delete config.el;
37871     if(config){
37872         if(typeof config == "boolean"){
37873             this.tabPosition = config ? "bottom" : "top";
37874         }else{
37875             Roo.apply(this, config);
37876         }
37877     }
37878     
37879     if(this.tabPosition == "bottom"){
37880         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37881         this.el.addClass("roo-tabs-bottom");
37882     }
37883     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37884     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37885     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37886     if(Roo.isIE){
37887         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37888     }
37889     if(this.tabPosition != "bottom"){
37890         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37891          * @type Roo.Element
37892          */
37893         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37894         this.el.addClass("roo-tabs-top");
37895     }
37896     this.items = [];
37897
37898     this.bodyEl.setStyle("position", "relative");
37899
37900     this.active = null;
37901     this.activateDelegate = this.activate.createDelegate(this);
37902
37903     this.addEvents({
37904         /**
37905          * @event tabchange
37906          * Fires when the active tab changes
37907          * @param {Roo.TabPanel} this
37908          * @param {Roo.TabPanelItem} activePanel The new active tab
37909          */
37910         "tabchange": true,
37911         /**
37912          * @event beforetabchange
37913          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37914          * @param {Roo.TabPanel} this
37915          * @param {Object} e Set cancel to true on this object to cancel the tab change
37916          * @param {Roo.TabPanelItem} tab The tab being changed to
37917          */
37918         "beforetabchange" : true
37919     });
37920
37921     Roo.EventManager.onWindowResize(this.onResize, this);
37922     this.cpad = this.el.getPadding("lr");
37923     this.hiddenCount = 0;
37924
37925
37926     // toolbar on the tabbar support...
37927     if (this.toolbar) {
37928         alert("no toolbar support yet");
37929         this.toolbar  = false;
37930         /*
37931         var tcfg = this.toolbar;
37932         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37933         this.toolbar = new Roo.Toolbar(tcfg);
37934         if (Roo.isSafari) {
37935             var tbl = tcfg.container.child('table', true);
37936             tbl.setAttribute('width', '100%');
37937         }
37938         */
37939         
37940     }
37941    
37942
37943
37944     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37945 };
37946
37947 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37948     /*
37949      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37950      */
37951     tabPosition : "top",
37952     /*
37953      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37954      */
37955     currentTabWidth : 0,
37956     /*
37957      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37958      */
37959     minTabWidth : 40,
37960     /*
37961      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37962      */
37963     maxTabWidth : 250,
37964     /*
37965      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37966      */
37967     preferredTabWidth : 175,
37968     /*
37969      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37970      */
37971     resizeTabs : false,
37972     /*
37973      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37974      */
37975     monitorResize : true,
37976     /*
37977      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37978      */
37979     toolbar : false,
37980
37981     /**
37982      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37983      * @param {String} id The id of the div to use <b>or create</b>
37984      * @param {String} text The text for the tab
37985      * @param {String} content (optional) Content to put in the TabPanelItem body
37986      * @param {Boolean} closable (optional) True to create a close icon on the tab
37987      * @return {Roo.TabPanelItem} The created TabPanelItem
37988      */
37989     addTab : function(id, text, content, closable, tpl)
37990     {
37991         var item = new Roo.bootstrap.panel.TabItem({
37992             panel: this,
37993             id : id,
37994             text : text,
37995             closable : closable,
37996             tpl : tpl
37997         });
37998         this.addTabItem(item);
37999         if(content){
38000             item.setContent(content);
38001         }
38002         return item;
38003     },
38004
38005     /**
38006      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38007      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38008      * @return {Roo.TabPanelItem}
38009      */
38010     getTab : function(id){
38011         return this.items[id];
38012     },
38013
38014     /**
38015      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38016      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38017      */
38018     hideTab : function(id){
38019         var t = this.items[id];
38020         if(!t.isHidden()){
38021            t.setHidden(true);
38022            this.hiddenCount++;
38023            this.autoSizeTabs();
38024         }
38025     },
38026
38027     /**
38028      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38029      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38030      */
38031     unhideTab : function(id){
38032         var t = this.items[id];
38033         if(t.isHidden()){
38034            t.setHidden(false);
38035            this.hiddenCount--;
38036            this.autoSizeTabs();
38037         }
38038     },
38039
38040     /**
38041      * Adds an existing {@link Roo.TabPanelItem}.
38042      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38043      */
38044     addTabItem : function(item){
38045         this.items[item.id] = item;
38046         this.items.push(item);
38047       //  if(this.resizeTabs){
38048     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38049   //         this.autoSizeTabs();
38050 //        }else{
38051 //            item.autoSize();
38052        // }
38053     },
38054
38055     /**
38056      * Removes a {@link Roo.TabPanelItem}.
38057      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38058      */
38059     removeTab : function(id){
38060         var items = this.items;
38061         var tab = items[id];
38062         if(!tab) { return; }
38063         var index = items.indexOf(tab);
38064         if(this.active == tab && items.length > 1){
38065             var newTab = this.getNextAvailable(index);
38066             if(newTab) {
38067                 newTab.activate();
38068             }
38069         }
38070         this.stripEl.dom.removeChild(tab.pnode.dom);
38071         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38072             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38073         }
38074         items.splice(index, 1);
38075         delete this.items[tab.id];
38076         tab.fireEvent("close", tab);
38077         tab.purgeListeners();
38078         this.autoSizeTabs();
38079     },
38080
38081     getNextAvailable : function(start){
38082         var items = this.items;
38083         var index = start;
38084         // look for a next tab that will slide over to
38085         // replace the one being removed
38086         while(index < items.length){
38087             var item = items[++index];
38088             if(item && !item.isHidden()){
38089                 return item;
38090             }
38091         }
38092         // if one isn't found select the previous tab (on the left)
38093         index = start;
38094         while(index >= 0){
38095             var item = items[--index];
38096             if(item && !item.isHidden()){
38097                 return item;
38098             }
38099         }
38100         return null;
38101     },
38102
38103     /**
38104      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38105      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38106      */
38107     disableTab : function(id){
38108         var tab = this.items[id];
38109         if(tab && this.active != tab){
38110             tab.disable();
38111         }
38112     },
38113
38114     /**
38115      * Enables a {@link Roo.TabPanelItem} that is disabled.
38116      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38117      */
38118     enableTab : function(id){
38119         var tab = this.items[id];
38120         tab.enable();
38121     },
38122
38123     /**
38124      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38125      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38126      * @return {Roo.TabPanelItem} The TabPanelItem.
38127      */
38128     activate : function(id){
38129         var tab = this.items[id];
38130         if(!tab){
38131             return null;
38132         }
38133         if(tab == this.active || tab.disabled){
38134             return tab;
38135         }
38136         var e = {};
38137         this.fireEvent("beforetabchange", this, e, tab);
38138         if(e.cancel !== true && !tab.disabled){
38139             if(this.active){
38140                 this.active.hide();
38141             }
38142             this.active = this.items[id];
38143             this.active.show();
38144             this.fireEvent("tabchange", this, this.active);
38145         }
38146         return tab;
38147     },
38148
38149     /**
38150      * Gets the active {@link Roo.TabPanelItem}.
38151      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38152      */
38153     getActiveTab : function(){
38154         return this.active;
38155     },
38156
38157     /**
38158      * Updates the tab body element to fit the height of the container element
38159      * for overflow scrolling
38160      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38161      */
38162     syncHeight : function(targetHeight){
38163         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38164         var bm = this.bodyEl.getMargins();
38165         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38166         this.bodyEl.setHeight(newHeight);
38167         return newHeight;
38168     },
38169
38170     onResize : function(){
38171         if(this.monitorResize){
38172             this.autoSizeTabs();
38173         }
38174     },
38175
38176     /**
38177      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38178      */
38179     beginUpdate : function(){
38180         this.updating = true;
38181     },
38182
38183     /**
38184      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38185      */
38186     endUpdate : function(){
38187         this.updating = false;
38188         this.autoSizeTabs();
38189     },
38190
38191     /**
38192      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38193      */
38194     autoSizeTabs : function(){
38195         var count = this.items.length;
38196         var vcount = count - this.hiddenCount;
38197         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38198             return;
38199         }
38200         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38201         var availWidth = Math.floor(w / vcount);
38202         var b = this.stripBody;
38203         if(b.getWidth() > w){
38204             var tabs = this.items;
38205             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38206             if(availWidth < this.minTabWidth){
38207                 /*if(!this.sleft){    // incomplete scrolling code
38208                     this.createScrollButtons();
38209                 }
38210                 this.showScroll();
38211                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38212             }
38213         }else{
38214             if(this.currentTabWidth < this.preferredTabWidth){
38215                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38216             }
38217         }
38218     },
38219
38220     /**
38221      * Returns the number of tabs in this TabPanel.
38222      * @return {Number}
38223      */
38224      getCount : function(){
38225          return this.items.length;
38226      },
38227
38228     /**
38229      * Resizes all the tabs to the passed width
38230      * @param {Number} The new width
38231      */
38232     setTabWidth : function(width){
38233         this.currentTabWidth = width;
38234         for(var i = 0, len = this.items.length; i < len; i++) {
38235                 if(!this.items[i].isHidden()) {
38236                 this.items[i].setWidth(width);
38237             }
38238         }
38239     },
38240
38241     /**
38242      * Destroys this TabPanel
38243      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38244      */
38245     destroy : function(removeEl){
38246         Roo.EventManager.removeResizeListener(this.onResize, this);
38247         for(var i = 0, len = this.items.length; i < len; i++){
38248             this.items[i].purgeListeners();
38249         }
38250         if(removeEl === true){
38251             this.el.update("");
38252             this.el.remove();
38253         }
38254     },
38255     
38256     createStrip : function(container)
38257     {
38258         var strip = document.createElement("nav");
38259         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
38260         container.appendChild(strip);
38261         return strip;
38262     },
38263     
38264     createStripList : function(strip)
38265     {
38266         // div wrapper for retard IE
38267         // returns the "tr" element.
38268         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38269         //'<div class="x-tabs-strip-wrap">'+
38270           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38271           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38272         return strip.firstChild; //.firstChild.firstChild.firstChild;
38273     },
38274     createBody : function(container)
38275     {
38276         var body = document.createElement("div");
38277         Roo.id(body, "tab-body");
38278         //Roo.fly(body).addClass("x-tabs-body");
38279         Roo.fly(body).addClass("tab-content");
38280         container.appendChild(body);
38281         return body;
38282     },
38283     createItemBody :function(bodyEl, id){
38284         var body = Roo.getDom(id);
38285         if(!body){
38286             body = document.createElement("div");
38287             body.id = id;
38288         }
38289         //Roo.fly(body).addClass("x-tabs-item-body");
38290         Roo.fly(body).addClass("tab-pane");
38291          bodyEl.insertBefore(body, bodyEl.firstChild);
38292         return body;
38293     },
38294     /** @private */
38295     createStripElements :  function(stripEl, text, closable, tpl)
38296     {
38297         var td = document.createElement("li"); // was td..
38298         
38299         
38300         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38301         
38302         
38303         stripEl.appendChild(td);
38304         /*if(closable){
38305             td.className = "x-tabs-closable";
38306             if(!this.closeTpl){
38307                 this.closeTpl = new Roo.Template(
38308                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38309                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38310                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38311                 );
38312             }
38313             var el = this.closeTpl.overwrite(td, {"text": text});
38314             var close = el.getElementsByTagName("div")[0];
38315             var inner = el.getElementsByTagName("em")[0];
38316             return {"el": el, "close": close, "inner": inner};
38317         } else {
38318         */
38319         // not sure what this is..
38320 //            if(!this.tabTpl){
38321                 //this.tabTpl = new Roo.Template(
38322                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38323                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38324                 //);
38325 //                this.tabTpl = new Roo.Template(
38326 //                   '<a href="#">' +
38327 //                   '<span unselectable="on"' +
38328 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38329 //                            ' >{text}</span></a>'
38330 //                );
38331 //                
38332 //            }
38333
38334
38335             var template = tpl || this.tabTpl || false;
38336             
38337             if(!template){
38338                 
38339                 template = new Roo.Template(
38340                    '<a href="#">' +
38341                    '<span unselectable="on"' +
38342                             (this.disableTooltips ? '' : ' title="{text}"') +
38343                             ' >{text}</span></a>'
38344                 );
38345             }
38346             
38347             switch (typeof(template)) {
38348                 case 'object' :
38349                     break;
38350                 case 'string' :
38351                     template = new Roo.Template(template);
38352                     break;
38353                 default :
38354                     break;
38355             }
38356             
38357             var el = template.overwrite(td, {"text": text});
38358             
38359             var inner = el.getElementsByTagName("span")[0];
38360             
38361             return {"el": el, "inner": inner};
38362             
38363     }
38364         
38365     
38366 });
38367
38368 /**
38369  * @class Roo.TabPanelItem
38370  * @extends Roo.util.Observable
38371  * Represents an individual item (tab plus body) in a TabPanel.
38372  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38373  * @param {String} id The id of this TabPanelItem
38374  * @param {String} text The text for the tab of this TabPanelItem
38375  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38376  */
38377 Roo.bootstrap.panel.TabItem = function(config){
38378     /**
38379      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38380      * @type Roo.TabPanel
38381      */
38382     this.tabPanel = config.panel;
38383     /**
38384      * The id for this TabPanelItem
38385      * @type String
38386      */
38387     this.id = config.id;
38388     /** @private */
38389     this.disabled = false;
38390     /** @private */
38391     this.text = config.text;
38392     /** @private */
38393     this.loaded = false;
38394     this.closable = config.closable;
38395
38396     /**
38397      * The body element for this TabPanelItem.
38398      * @type Roo.Element
38399      */
38400     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38401     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38402     this.bodyEl.setStyle("display", "block");
38403     this.bodyEl.setStyle("zoom", "1");
38404     //this.hideAction();
38405
38406     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38407     /** @private */
38408     this.el = Roo.get(els.el);
38409     this.inner = Roo.get(els.inner, true);
38410     this.textEl = Roo.get(this.el.dom.firstChild, true);
38411     this.pnode = Roo.get(els.el.parentNode, true);
38412 //    this.el.on("mousedown", this.onTabMouseDown, this);
38413     this.el.on("click", this.onTabClick, this);
38414     /** @private */
38415     if(config.closable){
38416         var c = Roo.get(els.close, true);
38417         c.dom.title = this.closeText;
38418         c.addClassOnOver("close-over");
38419         c.on("click", this.closeClick, this);
38420      }
38421
38422     this.addEvents({
38423          /**
38424          * @event activate
38425          * Fires when this tab becomes the active tab.
38426          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38427          * @param {Roo.TabPanelItem} this
38428          */
38429         "activate": true,
38430         /**
38431          * @event beforeclose
38432          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38433          * @param {Roo.TabPanelItem} this
38434          * @param {Object} e Set cancel to true on this object to cancel the close.
38435          */
38436         "beforeclose": true,
38437         /**
38438          * @event close
38439          * Fires when this tab is closed.
38440          * @param {Roo.TabPanelItem} this
38441          */
38442          "close": true,
38443         /**
38444          * @event deactivate
38445          * Fires when this tab is no longer the active tab.
38446          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38447          * @param {Roo.TabPanelItem} this
38448          */
38449          "deactivate" : true
38450     });
38451     this.hidden = false;
38452
38453     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38454 };
38455
38456 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38457            {
38458     purgeListeners : function(){
38459        Roo.util.Observable.prototype.purgeListeners.call(this);
38460        this.el.removeAllListeners();
38461     },
38462     /**
38463      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38464      */
38465     show : function(){
38466         this.pnode.addClass("active");
38467         this.showAction();
38468         if(Roo.isOpera){
38469             this.tabPanel.stripWrap.repaint();
38470         }
38471         this.fireEvent("activate", this.tabPanel, this);
38472     },
38473
38474     /**
38475      * Returns true if this tab is the active tab.
38476      * @return {Boolean}
38477      */
38478     isActive : function(){
38479         return this.tabPanel.getActiveTab() == this;
38480     },
38481
38482     /**
38483      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38484      */
38485     hide : function(){
38486         this.pnode.removeClass("active");
38487         this.hideAction();
38488         this.fireEvent("deactivate", this.tabPanel, this);
38489     },
38490
38491     hideAction : function(){
38492         this.bodyEl.hide();
38493         this.bodyEl.setStyle("position", "absolute");
38494         this.bodyEl.setLeft("-20000px");
38495         this.bodyEl.setTop("-20000px");
38496     },
38497
38498     showAction : function(){
38499         this.bodyEl.setStyle("position", "relative");
38500         this.bodyEl.setTop("");
38501         this.bodyEl.setLeft("");
38502         this.bodyEl.show();
38503     },
38504
38505     /**
38506      * Set the tooltip for the tab.
38507      * @param {String} tooltip The tab's tooltip
38508      */
38509     setTooltip : function(text){
38510         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38511             this.textEl.dom.qtip = text;
38512             this.textEl.dom.removeAttribute('title');
38513         }else{
38514             this.textEl.dom.title = text;
38515         }
38516     },
38517
38518     onTabClick : function(e){
38519         e.preventDefault();
38520         this.tabPanel.activate(this.id);
38521     },
38522
38523     onTabMouseDown : function(e){
38524         e.preventDefault();
38525         this.tabPanel.activate(this.id);
38526     },
38527 /*
38528     getWidth : function(){
38529         return this.inner.getWidth();
38530     },
38531
38532     setWidth : function(width){
38533         var iwidth = width - this.pnode.getPadding("lr");
38534         this.inner.setWidth(iwidth);
38535         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38536         this.pnode.setWidth(width);
38537     },
38538 */
38539     /**
38540      * Show or hide the tab
38541      * @param {Boolean} hidden True to hide or false to show.
38542      */
38543     setHidden : function(hidden){
38544         this.hidden = hidden;
38545         this.pnode.setStyle("display", hidden ? "none" : "");
38546     },
38547
38548     /**
38549      * Returns true if this tab is "hidden"
38550      * @return {Boolean}
38551      */
38552     isHidden : function(){
38553         return this.hidden;
38554     },
38555
38556     /**
38557      * Returns the text for this tab
38558      * @return {String}
38559      */
38560     getText : function(){
38561         return this.text;
38562     },
38563     /*
38564     autoSize : function(){
38565         //this.el.beginMeasure();
38566         this.textEl.setWidth(1);
38567         /*
38568          *  #2804 [new] Tabs in Roojs
38569          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38570          */
38571         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38572         //this.el.endMeasure();
38573     //},
38574
38575     /**
38576      * Sets the text for the tab (Note: this also sets the tooltip text)
38577      * @param {String} text The tab's text and tooltip
38578      */
38579     setText : function(text){
38580         this.text = text;
38581         this.textEl.update(text);
38582         this.setTooltip(text);
38583         //if(!this.tabPanel.resizeTabs){
38584         //    this.autoSize();
38585         //}
38586     },
38587     /**
38588      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38589      */
38590     activate : function(){
38591         this.tabPanel.activate(this.id);
38592     },
38593
38594     /**
38595      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38596      */
38597     disable : function(){
38598         if(this.tabPanel.active != this){
38599             this.disabled = true;
38600             this.pnode.addClass("disabled");
38601         }
38602     },
38603
38604     /**
38605      * Enables this TabPanelItem if it was previously disabled.
38606      */
38607     enable : function(){
38608         this.disabled = false;
38609         this.pnode.removeClass("disabled");
38610     },
38611
38612     /**
38613      * Sets the content for this TabPanelItem.
38614      * @param {String} content The content
38615      * @param {Boolean} loadScripts true to look for and load scripts
38616      */
38617     setContent : function(content, loadScripts){
38618         this.bodyEl.update(content, loadScripts);
38619     },
38620
38621     /**
38622      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38623      * @return {Roo.UpdateManager} The UpdateManager
38624      */
38625     getUpdateManager : function(){
38626         return this.bodyEl.getUpdateManager();
38627     },
38628
38629     /**
38630      * Set a URL to be used to load the content for this TabPanelItem.
38631      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38632      * @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)
38633      * @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)
38634      * @return {Roo.UpdateManager} The UpdateManager
38635      */
38636     setUrl : function(url, params, loadOnce){
38637         if(this.refreshDelegate){
38638             this.un('activate', this.refreshDelegate);
38639         }
38640         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38641         this.on("activate", this.refreshDelegate);
38642         return this.bodyEl.getUpdateManager();
38643     },
38644
38645     /** @private */
38646     _handleRefresh : function(url, params, loadOnce){
38647         if(!loadOnce || !this.loaded){
38648             var updater = this.bodyEl.getUpdateManager();
38649             updater.update(url, params, this._setLoaded.createDelegate(this));
38650         }
38651     },
38652
38653     /**
38654      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38655      *   Will fail silently if the setUrl method has not been called.
38656      *   This does not activate the panel, just updates its content.
38657      */
38658     refresh : function(){
38659         if(this.refreshDelegate){
38660            this.loaded = false;
38661            this.refreshDelegate();
38662         }
38663     },
38664
38665     /** @private */
38666     _setLoaded : function(){
38667         this.loaded = true;
38668     },
38669
38670     /** @private */
38671     closeClick : function(e){
38672         var o = {};
38673         e.stopEvent();
38674         this.fireEvent("beforeclose", this, o);
38675         if(o.cancel !== true){
38676             this.tabPanel.removeTab(this.id);
38677         }
38678     },
38679     /**
38680      * The text displayed in the tooltip for the close icon.
38681      * @type String
38682      */
38683     closeText : "Close this tab"
38684 });
38685 /**
38686 *    This script refer to:
38687 *    Title: International Telephone Input
38688 *    Author: Jack O'Connor
38689 *    Code version:  v12.1.12
38690 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38691 **/
38692
38693 Roo.bootstrap.PhoneInputData = function() {
38694     var d = [
38695       [
38696         "Afghanistan (‫افغانستان‬‎)",
38697         "af",
38698         "93"
38699       ],
38700       [
38701         "Albania (Shqipëri)",
38702         "al",
38703         "355"
38704       ],
38705       [
38706         "Algeria (‫الجزائر‬‎)",
38707         "dz",
38708         "213"
38709       ],
38710       [
38711         "American Samoa",
38712         "as",
38713         "1684"
38714       ],
38715       [
38716         "Andorra",
38717         "ad",
38718         "376"
38719       ],
38720       [
38721         "Angola",
38722         "ao",
38723         "244"
38724       ],
38725       [
38726         "Anguilla",
38727         "ai",
38728         "1264"
38729       ],
38730       [
38731         "Antigua and Barbuda",
38732         "ag",
38733         "1268"
38734       ],
38735       [
38736         "Argentina",
38737         "ar",
38738         "54"
38739       ],
38740       [
38741         "Armenia (Հայաստան)",
38742         "am",
38743         "374"
38744       ],
38745       [
38746         "Aruba",
38747         "aw",
38748         "297"
38749       ],
38750       [
38751         "Australia",
38752         "au",
38753         "61",
38754         0
38755       ],
38756       [
38757         "Austria (Österreich)",
38758         "at",
38759         "43"
38760       ],
38761       [
38762         "Azerbaijan (Azərbaycan)",
38763         "az",
38764         "994"
38765       ],
38766       [
38767         "Bahamas",
38768         "bs",
38769         "1242"
38770       ],
38771       [
38772         "Bahrain (‫البحرين‬‎)",
38773         "bh",
38774         "973"
38775       ],
38776       [
38777         "Bangladesh (বাংলাদেশ)",
38778         "bd",
38779         "880"
38780       ],
38781       [
38782         "Barbados",
38783         "bb",
38784         "1246"
38785       ],
38786       [
38787         "Belarus (Беларусь)",
38788         "by",
38789         "375"
38790       ],
38791       [
38792         "Belgium (België)",
38793         "be",
38794         "32"
38795       ],
38796       [
38797         "Belize",
38798         "bz",
38799         "501"
38800       ],
38801       [
38802         "Benin (Bénin)",
38803         "bj",
38804         "229"
38805       ],
38806       [
38807         "Bermuda",
38808         "bm",
38809         "1441"
38810       ],
38811       [
38812         "Bhutan (འབྲུག)",
38813         "bt",
38814         "975"
38815       ],
38816       [
38817         "Bolivia",
38818         "bo",
38819         "591"
38820       ],
38821       [
38822         "Bosnia and Herzegovina (Босна и Херцеговина)",
38823         "ba",
38824         "387"
38825       ],
38826       [
38827         "Botswana",
38828         "bw",
38829         "267"
38830       ],
38831       [
38832         "Brazil (Brasil)",
38833         "br",
38834         "55"
38835       ],
38836       [
38837         "British Indian Ocean Territory",
38838         "io",
38839         "246"
38840       ],
38841       [
38842         "British Virgin Islands",
38843         "vg",
38844         "1284"
38845       ],
38846       [
38847         "Brunei",
38848         "bn",
38849         "673"
38850       ],
38851       [
38852         "Bulgaria (България)",
38853         "bg",
38854         "359"
38855       ],
38856       [
38857         "Burkina Faso",
38858         "bf",
38859         "226"
38860       ],
38861       [
38862         "Burundi (Uburundi)",
38863         "bi",
38864         "257"
38865       ],
38866       [
38867         "Cambodia (កម្ពុជា)",
38868         "kh",
38869         "855"
38870       ],
38871       [
38872         "Cameroon (Cameroun)",
38873         "cm",
38874         "237"
38875       ],
38876       [
38877         "Canada",
38878         "ca",
38879         "1",
38880         1,
38881         ["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"]
38882       ],
38883       [
38884         "Cape Verde (Kabu Verdi)",
38885         "cv",
38886         "238"
38887       ],
38888       [
38889         "Caribbean Netherlands",
38890         "bq",
38891         "599",
38892         1
38893       ],
38894       [
38895         "Cayman Islands",
38896         "ky",
38897         "1345"
38898       ],
38899       [
38900         "Central African Republic (République centrafricaine)",
38901         "cf",
38902         "236"
38903       ],
38904       [
38905         "Chad (Tchad)",
38906         "td",
38907         "235"
38908       ],
38909       [
38910         "Chile",
38911         "cl",
38912         "56"
38913       ],
38914       [
38915         "China (中国)",
38916         "cn",
38917         "86"
38918       ],
38919       [
38920         "Christmas Island",
38921         "cx",
38922         "61",
38923         2
38924       ],
38925       [
38926         "Cocos (Keeling) Islands",
38927         "cc",
38928         "61",
38929         1
38930       ],
38931       [
38932         "Colombia",
38933         "co",
38934         "57"
38935       ],
38936       [
38937         "Comoros (‫جزر القمر‬‎)",
38938         "km",
38939         "269"
38940       ],
38941       [
38942         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38943         "cd",
38944         "243"
38945       ],
38946       [
38947         "Congo (Republic) (Congo-Brazzaville)",
38948         "cg",
38949         "242"
38950       ],
38951       [
38952         "Cook Islands",
38953         "ck",
38954         "682"
38955       ],
38956       [
38957         "Costa Rica",
38958         "cr",
38959         "506"
38960       ],
38961       [
38962         "Côte d’Ivoire",
38963         "ci",
38964         "225"
38965       ],
38966       [
38967         "Croatia (Hrvatska)",
38968         "hr",
38969         "385"
38970       ],
38971       [
38972         "Cuba",
38973         "cu",
38974         "53"
38975       ],
38976       [
38977         "Curaçao",
38978         "cw",
38979         "599",
38980         0
38981       ],
38982       [
38983         "Cyprus (Κύπρος)",
38984         "cy",
38985         "357"
38986       ],
38987       [
38988         "Czech Republic (Česká republika)",
38989         "cz",
38990         "420"
38991       ],
38992       [
38993         "Denmark (Danmark)",
38994         "dk",
38995         "45"
38996       ],
38997       [
38998         "Djibouti",
38999         "dj",
39000         "253"
39001       ],
39002       [
39003         "Dominica",
39004         "dm",
39005         "1767"
39006       ],
39007       [
39008         "Dominican Republic (República Dominicana)",
39009         "do",
39010         "1",
39011         2,
39012         ["809", "829", "849"]
39013       ],
39014       [
39015         "Ecuador",
39016         "ec",
39017         "593"
39018       ],
39019       [
39020         "Egypt (‫مصر‬‎)",
39021         "eg",
39022         "20"
39023       ],
39024       [
39025         "El Salvador",
39026         "sv",
39027         "503"
39028       ],
39029       [
39030         "Equatorial Guinea (Guinea Ecuatorial)",
39031         "gq",
39032         "240"
39033       ],
39034       [
39035         "Eritrea",
39036         "er",
39037         "291"
39038       ],
39039       [
39040         "Estonia (Eesti)",
39041         "ee",
39042         "372"
39043       ],
39044       [
39045         "Ethiopia",
39046         "et",
39047         "251"
39048       ],
39049       [
39050         "Falkland Islands (Islas Malvinas)",
39051         "fk",
39052         "500"
39053       ],
39054       [
39055         "Faroe Islands (Føroyar)",
39056         "fo",
39057         "298"
39058       ],
39059       [
39060         "Fiji",
39061         "fj",
39062         "679"
39063       ],
39064       [
39065         "Finland (Suomi)",
39066         "fi",
39067         "358",
39068         0
39069       ],
39070       [
39071         "France",
39072         "fr",
39073         "33"
39074       ],
39075       [
39076         "French Guiana (Guyane française)",
39077         "gf",
39078         "594"
39079       ],
39080       [
39081         "French Polynesia (Polynésie française)",
39082         "pf",
39083         "689"
39084       ],
39085       [
39086         "Gabon",
39087         "ga",
39088         "241"
39089       ],
39090       [
39091         "Gambia",
39092         "gm",
39093         "220"
39094       ],
39095       [
39096         "Georgia (საქართველო)",
39097         "ge",
39098         "995"
39099       ],
39100       [
39101         "Germany (Deutschland)",
39102         "de",
39103         "49"
39104       ],
39105       [
39106         "Ghana (Gaana)",
39107         "gh",
39108         "233"
39109       ],
39110       [
39111         "Gibraltar",
39112         "gi",
39113         "350"
39114       ],
39115       [
39116         "Greece (Ελλάδα)",
39117         "gr",
39118         "30"
39119       ],
39120       [
39121         "Greenland (Kalaallit Nunaat)",
39122         "gl",
39123         "299"
39124       ],
39125       [
39126         "Grenada",
39127         "gd",
39128         "1473"
39129       ],
39130       [
39131         "Guadeloupe",
39132         "gp",
39133         "590",
39134         0
39135       ],
39136       [
39137         "Guam",
39138         "gu",
39139         "1671"
39140       ],
39141       [
39142         "Guatemala",
39143         "gt",
39144         "502"
39145       ],
39146       [
39147         "Guernsey",
39148         "gg",
39149         "44",
39150         1
39151       ],
39152       [
39153         "Guinea (Guinée)",
39154         "gn",
39155         "224"
39156       ],
39157       [
39158         "Guinea-Bissau (Guiné Bissau)",
39159         "gw",
39160         "245"
39161       ],
39162       [
39163         "Guyana",
39164         "gy",
39165         "592"
39166       ],
39167       [
39168         "Haiti",
39169         "ht",
39170         "509"
39171       ],
39172       [
39173         "Honduras",
39174         "hn",
39175         "504"
39176       ],
39177       [
39178         "Hong Kong (香港)",
39179         "hk",
39180         "852"
39181       ],
39182       [
39183         "Hungary (Magyarország)",
39184         "hu",
39185         "36"
39186       ],
39187       [
39188         "Iceland (Ísland)",
39189         "is",
39190         "354"
39191       ],
39192       [
39193         "India (भारत)",
39194         "in",
39195         "91"
39196       ],
39197       [
39198         "Indonesia",
39199         "id",
39200         "62"
39201       ],
39202       [
39203         "Iran (‫ایران‬‎)",
39204         "ir",
39205         "98"
39206       ],
39207       [
39208         "Iraq (‫العراق‬‎)",
39209         "iq",
39210         "964"
39211       ],
39212       [
39213         "Ireland",
39214         "ie",
39215         "353"
39216       ],
39217       [
39218         "Isle of Man",
39219         "im",
39220         "44",
39221         2
39222       ],
39223       [
39224         "Israel (‫ישראל‬‎)",
39225         "il",
39226         "972"
39227       ],
39228       [
39229         "Italy (Italia)",
39230         "it",
39231         "39",
39232         0
39233       ],
39234       [
39235         "Jamaica",
39236         "jm",
39237         "1876"
39238       ],
39239       [
39240         "Japan (日本)",
39241         "jp",
39242         "81"
39243       ],
39244       [
39245         "Jersey",
39246         "je",
39247         "44",
39248         3
39249       ],
39250       [
39251         "Jordan (‫الأردن‬‎)",
39252         "jo",
39253         "962"
39254       ],
39255       [
39256         "Kazakhstan (Казахстан)",
39257         "kz",
39258         "7",
39259         1
39260       ],
39261       [
39262         "Kenya",
39263         "ke",
39264         "254"
39265       ],
39266       [
39267         "Kiribati",
39268         "ki",
39269         "686"
39270       ],
39271       [
39272         "Kosovo",
39273         "xk",
39274         "383"
39275       ],
39276       [
39277         "Kuwait (‫الكويت‬‎)",
39278         "kw",
39279         "965"
39280       ],
39281       [
39282         "Kyrgyzstan (Кыргызстан)",
39283         "kg",
39284         "996"
39285       ],
39286       [
39287         "Laos (ລາວ)",
39288         "la",
39289         "856"
39290       ],
39291       [
39292         "Latvia (Latvija)",
39293         "lv",
39294         "371"
39295       ],
39296       [
39297         "Lebanon (‫لبنان‬‎)",
39298         "lb",
39299         "961"
39300       ],
39301       [
39302         "Lesotho",
39303         "ls",
39304         "266"
39305       ],
39306       [
39307         "Liberia",
39308         "lr",
39309         "231"
39310       ],
39311       [
39312         "Libya (‫ليبيا‬‎)",
39313         "ly",
39314         "218"
39315       ],
39316       [
39317         "Liechtenstein",
39318         "li",
39319         "423"
39320       ],
39321       [
39322         "Lithuania (Lietuva)",
39323         "lt",
39324         "370"
39325       ],
39326       [
39327         "Luxembourg",
39328         "lu",
39329         "352"
39330       ],
39331       [
39332         "Macau (澳門)",
39333         "mo",
39334         "853"
39335       ],
39336       [
39337         "Macedonia (FYROM) (Македонија)",
39338         "mk",
39339         "389"
39340       ],
39341       [
39342         "Madagascar (Madagasikara)",
39343         "mg",
39344         "261"
39345       ],
39346       [
39347         "Malawi",
39348         "mw",
39349         "265"
39350       ],
39351       [
39352         "Malaysia",
39353         "my",
39354         "60"
39355       ],
39356       [
39357         "Maldives",
39358         "mv",
39359         "960"
39360       ],
39361       [
39362         "Mali",
39363         "ml",
39364         "223"
39365       ],
39366       [
39367         "Malta",
39368         "mt",
39369         "356"
39370       ],
39371       [
39372         "Marshall Islands",
39373         "mh",
39374         "692"
39375       ],
39376       [
39377         "Martinique",
39378         "mq",
39379         "596"
39380       ],
39381       [
39382         "Mauritania (‫موريتانيا‬‎)",
39383         "mr",
39384         "222"
39385       ],
39386       [
39387         "Mauritius (Moris)",
39388         "mu",
39389         "230"
39390       ],
39391       [
39392         "Mayotte",
39393         "yt",
39394         "262",
39395         1
39396       ],
39397       [
39398         "Mexico (México)",
39399         "mx",
39400         "52"
39401       ],
39402       [
39403         "Micronesia",
39404         "fm",
39405         "691"
39406       ],
39407       [
39408         "Moldova (Republica Moldova)",
39409         "md",
39410         "373"
39411       ],
39412       [
39413         "Monaco",
39414         "mc",
39415         "377"
39416       ],
39417       [
39418         "Mongolia (Монгол)",
39419         "mn",
39420         "976"
39421       ],
39422       [
39423         "Montenegro (Crna Gora)",
39424         "me",
39425         "382"
39426       ],
39427       [
39428         "Montserrat",
39429         "ms",
39430         "1664"
39431       ],
39432       [
39433         "Morocco (‫المغرب‬‎)",
39434         "ma",
39435         "212",
39436         0
39437       ],
39438       [
39439         "Mozambique (Moçambique)",
39440         "mz",
39441         "258"
39442       ],
39443       [
39444         "Myanmar (Burma) (မြန်မာ)",
39445         "mm",
39446         "95"
39447       ],
39448       [
39449         "Namibia (Namibië)",
39450         "na",
39451         "264"
39452       ],
39453       [
39454         "Nauru",
39455         "nr",
39456         "674"
39457       ],
39458       [
39459         "Nepal (नेपाल)",
39460         "np",
39461         "977"
39462       ],
39463       [
39464         "Netherlands (Nederland)",
39465         "nl",
39466         "31"
39467       ],
39468       [
39469         "New Caledonia (Nouvelle-Calédonie)",
39470         "nc",
39471         "687"
39472       ],
39473       [
39474         "New Zealand",
39475         "nz",
39476         "64"
39477       ],
39478       [
39479         "Nicaragua",
39480         "ni",
39481         "505"
39482       ],
39483       [
39484         "Niger (Nijar)",
39485         "ne",
39486         "227"
39487       ],
39488       [
39489         "Nigeria",
39490         "ng",
39491         "234"
39492       ],
39493       [
39494         "Niue",
39495         "nu",
39496         "683"
39497       ],
39498       [
39499         "Norfolk Island",
39500         "nf",
39501         "672"
39502       ],
39503       [
39504         "North Korea (조선 민주주의 인민 공화국)",
39505         "kp",
39506         "850"
39507       ],
39508       [
39509         "Northern Mariana Islands",
39510         "mp",
39511         "1670"
39512       ],
39513       [
39514         "Norway (Norge)",
39515         "no",
39516         "47",
39517         0
39518       ],
39519       [
39520         "Oman (‫عُمان‬‎)",
39521         "om",
39522         "968"
39523       ],
39524       [
39525         "Pakistan (‫پاکستان‬‎)",
39526         "pk",
39527         "92"
39528       ],
39529       [
39530         "Palau",
39531         "pw",
39532         "680"
39533       ],
39534       [
39535         "Palestine (‫فلسطين‬‎)",
39536         "ps",
39537         "970"
39538       ],
39539       [
39540         "Panama (Panamá)",
39541         "pa",
39542         "507"
39543       ],
39544       [
39545         "Papua New Guinea",
39546         "pg",
39547         "675"
39548       ],
39549       [
39550         "Paraguay",
39551         "py",
39552         "595"
39553       ],
39554       [
39555         "Peru (Perú)",
39556         "pe",
39557         "51"
39558       ],
39559       [
39560         "Philippines",
39561         "ph",
39562         "63"
39563       ],
39564       [
39565         "Poland (Polska)",
39566         "pl",
39567         "48"
39568       ],
39569       [
39570         "Portugal",
39571         "pt",
39572         "351"
39573       ],
39574       [
39575         "Puerto Rico",
39576         "pr",
39577         "1",
39578         3,
39579         ["787", "939"]
39580       ],
39581       [
39582         "Qatar (‫قطر‬‎)",
39583         "qa",
39584         "974"
39585       ],
39586       [
39587         "Réunion (La Réunion)",
39588         "re",
39589         "262",
39590         0
39591       ],
39592       [
39593         "Romania (România)",
39594         "ro",
39595         "40"
39596       ],
39597       [
39598         "Russia (Россия)",
39599         "ru",
39600         "7",
39601         0
39602       ],
39603       [
39604         "Rwanda",
39605         "rw",
39606         "250"
39607       ],
39608       [
39609         "Saint Barthélemy",
39610         "bl",
39611         "590",
39612         1
39613       ],
39614       [
39615         "Saint Helena",
39616         "sh",
39617         "290"
39618       ],
39619       [
39620         "Saint Kitts and Nevis",
39621         "kn",
39622         "1869"
39623       ],
39624       [
39625         "Saint Lucia",
39626         "lc",
39627         "1758"
39628       ],
39629       [
39630         "Saint Martin (Saint-Martin (partie française))",
39631         "mf",
39632         "590",
39633         2
39634       ],
39635       [
39636         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39637         "pm",
39638         "508"
39639       ],
39640       [
39641         "Saint Vincent and the Grenadines",
39642         "vc",
39643         "1784"
39644       ],
39645       [
39646         "Samoa",
39647         "ws",
39648         "685"
39649       ],
39650       [
39651         "San Marino",
39652         "sm",
39653         "378"
39654       ],
39655       [
39656         "São Tomé and Príncipe (São Tomé e Príncipe)",
39657         "st",
39658         "239"
39659       ],
39660       [
39661         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39662         "sa",
39663         "966"
39664       ],
39665       [
39666         "Senegal (Sénégal)",
39667         "sn",
39668         "221"
39669       ],
39670       [
39671         "Serbia (Србија)",
39672         "rs",
39673         "381"
39674       ],
39675       [
39676         "Seychelles",
39677         "sc",
39678         "248"
39679       ],
39680       [
39681         "Sierra Leone",
39682         "sl",
39683         "232"
39684       ],
39685       [
39686         "Singapore",
39687         "sg",
39688         "65"
39689       ],
39690       [
39691         "Sint Maarten",
39692         "sx",
39693         "1721"
39694       ],
39695       [
39696         "Slovakia (Slovensko)",
39697         "sk",
39698         "421"
39699       ],
39700       [
39701         "Slovenia (Slovenija)",
39702         "si",
39703         "386"
39704       ],
39705       [
39706         "Solomon Islands",
39707         "sb",
39708         "677"
39709       ],
39710       [
39711         "Somalia (Soomaaliya)",
39712         "so",
39713         "252"
39714       ],
39715       [
39716         "South Africa",
39717         "za",
39718         "27"
39719       ],
39720       [
39721         "South Korea (대한민국)",
39722         "kr",
39723         "82"
39724       ],
39725       [
39726         "South Sudan (‫جنوب السودان‬‎)",
39727         "ss",
39728         "211"
39729       ],
39730       [
39731         "Spain (España)",
39732         "es",
39733         "34"
39734       ],
39735       [
39736         "Sri Lanka (ශ්‍රී ලංකාව)",
39737         "lk",
39738         "94"
39739       ],
39740       [
39741         "Sudan (‫السودان‬‎)",
39742         "sd",
39743         "249"
39744       ],
39745       [
39746         "Suriname",
39747         "sr",
39748         "597"
39749       ],
39750       [
39751         "Svalbard and Jan Mayen",
39752         "sj",
39753         "47",
39754         1
39755       ],
39756       [
39757         "Swaziland",
39758         "sz",
39759         "268"
39760       ],
39761       [
39762         "Sweden (Sverige)",
39763         "se",
39764         "46"
39765       ],
39766       [
39767         "Switzerland (Schweiz)",
39768         "ch",
39769         "41"
39770       ],
39771       [
39772         "Syria (‫سوريا‬‎)",
39773         "sy",
39774         "963"
39775       ],
39776       [
39777         "Taiwan (台灣)",
39778         "tw",
39779         "886"
39780       ],
39781       [
39782         "Tajikistan",
39783         "tj",
39784         "992"
39785       ],
39786       [
39787         "Tanzania",
39788         "tz",
39789         "255"
39790       ],
39791       [
39792         "Thailand (ไทย)",
39793         "th",
39794         "66"
39795       ],
39796       [
39797         "Timor-Leste",
39798         "tl",
39799         "670"
39800       ],
39801       [
39802         "Togo",
39803         "tg",
39804         "228"
39805       ],
39806       [
39807         "Tokelau",
39808         "tk",
39809         "690"
39810       ],
39811       [
39812         "Tonga",
39813         "to",
39814         "676"
39815       ],
39816       [
39817         "Trinidad and Tobago",
39818         "tt",
39819         "1868"
39820       ],
39821       [
39822         "Tunisia (‫تونس‬‎)",
39823         "tn",
39824         "216"
39825       ],
39826       [
39827         "Turkey (Türkiye)",
39828         "tr",
39829         "90"
39830       ],
39831       [
39832         "Turkmenistan",
39833         "tm",
39834         "993"
39835       ],
39836       [
39837         "Turks and Caicos Islands",
39838         "tc",
39839         "1649"
39840       ],
39841       [
39842         "Tuvalu",
39843         "tv",
39844         "688"
39845       ],
39846       [
39847         "U.S. Virgin Islands",
39848         "vi",
39849         "1340"
39850       ],
39851       [
39852         "Uganda",
39853         "ug",
39854         "256"
39855       ],
39856       [
39857         "Ukraine (Україна)",
39858         "ua",
39859         "380"
39860       ],
39861       [
39862         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39863         "ae",
39864         "971"
39865       ],
39866       [
39867         "United Kingdom",
39868         "gb",
39869         "44",
39870         0
39871       ],
39872       [
39873         "United States",
39874         "us",
39875         "1",
39876         0
39877       ],
39878       [
39879         "Uruguay",
39880         "uy",
39881         "598"
39882       ],
39883       [
39884         "Uzbekistan (Oʻzbekiston)",
39885         "uz",
39886         "998"
39887       ],
39888       [
39889         "Vanuatu",
39890         "vu",
39891         "678"
39892       ],
39893       [
39894         "Vatican City (Città del Vaticano)",
39895         "va",
39896         "39",
39897         1
39898       ],
39899       [
39900         "Venezuela",
39901         "ve",
39902         "58"
39903       ],
39904       [
39905         "Vietnam (Việt Nam)",
39906         "vn",
39907         "84"
39908       ],
39909       [
39910         "Wallis and Futuna (Wallis-et-Futuna)",
39911         "wf",
39912         "681"
39913       ],
39914       [
39915         "Western Sahara (‫الصحراء الغربية‬‎)",
39916         "eh",
39917         "212",
39918         1
39919       ],
39920       [
39921         "Yemen (‫اليمن‬‎)",
39922         "ye",
39923         "967"
39924       ],
39925       [
39926         "Zambia",
39927         "zm",
39928         "260"
39929       ],
39930       [
39931         "Zimbabwe",
39932         "zw",
39933         "263"
39934       ],
39935       [
39936         "Åland Islands",
39937         "ax",
39938         "358",
39939         1
39940       ]
39941   ];
39942   
39943   return d;
39944 }/**
39945 *    This script refer to:
39946 *    Title: International Telephone Input
39947 *    Author: Jack O'Connor
39948 *    Code version:  v12.1.12
39949 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39950 **/
39951
39952 /**
39953  * @class Roo.bootstrap.PhoneInput
39954  * @extends Roo.bootstrap.TriggerField
39955  * An input with International dial-code selection
39956  
39957  * @cfg {String} defaultDialCode default '+852'
39958  * @cfg {Array} preferedCountries default []
39959   
39960  * @constructor
39961  * Create a new PhoneInput.
39962  * @param {Object} config Configuration options
39963  */
39964
39965 Roo.bootstrap.PhoneInput = function(config) {
39966     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39967 };
39968
39969 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39970         
39971         listWidth: undefined,
39972         
39973         selectedClass: 'active',
39974         
39975         invalidClass : "has-warning",
39976         
39977         validClass: 'has-success',
39978         
39979         allowed: '0123456789',
39980         
39981         max_length: 15,
39982         
39983         /**
39984          * @cfg {String} defaultDialCode The default dial code when initializing the input
39985          */
39986         defaultDialCode: '+852',
39987         
39988         /**
39989          * @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
39990          */
39991         preferedCountries: false,
39992         
39993         getAutoCreate : function()
39994         {
39995             var data = Roo.bootstrap.PhoneInputData();
39996             var align = this.labelAlign || this.parentLabelAlign();
39997             var id = Roo.id();
39998             
39999             this.allCountries = [];
40000             this.dialCodeMapping = [];
40001             
40002             for (var i = 0; i < data.length; i++) {
40003               var c = data[i];
40004               this.allCountries[i] = {
40005                 name: c[0],
40006                 iso2: c[1],
40007                 dialCode: c[2],
40008                 priority: c[3] || 0,
40009                 areaCodes: c[4] || null
40010               };
40011               this.dialCodeMapping[c[2]] = {
40012                   name: c[0],
40013                   iso2: c[1],
40014                   priority: c[3] || 0,
40015                   areaCodes: c[4] || null
40016               };
40017             }
40018             
40019             var cfg = {
40020                 cls: 'form-group',
40021                 cn: []
40022             };
40023             
40024             var input =  {
40025                 tag: 'input',
40026                 id : id,
40027                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40028                 maxlength: this.max_length,
40029                 cls : 'form-control tel-input',
40030                 autocomplete: 'new-password'
40031             };
40032             
40033             var hiddenInput = {
40034                 tag: 'input',
40035                 type: 'hidden',
40036                 cls: 'hidden-tel-input'
40037             };
40038             
40039             if (this.name) {
40040                 hiddenInput.name = this.name;
40041             }
40042             
40043             if (this.disabled) {
40044                 input.disabled = true;
40045             }
40046             
40047             var flag_container = {
40048                 tag: 'div',
40049                 cls: 'flag-box',
40050                 cn: [
40051                     {
40052                         tag: 'div',
40053                         cls: 'flag'
40054                     },
40055                     {
40056                         tag: 'div',
40057                         cls: 'caret'
40058                     }
40059                 ]
40060             };
40061             
40062             var box = {
40063                 tag: 'div',
40064                 cls: this.hasFeedback ? 'has-feedback' : '',
40065                 cn: [
40066                     hiddenInput,
40067                     input,
40068                     {
40069                         tag: 'input',
40070                         cls: 'dial-code-holder',
40071                         disabled: true
40072                     }
40073                 ]
40074             };
40075             
40076             var container = {
40077                 cls: 'roo-select2-container input-group',
40078                 cn: [
40079                     flag_container,
40080                     box
40081                 ]
40082             };
40083             
40084             if (this.fieldLabel.length) {
40085                 var indicator = {
40086                     tag: 'i',
40087                     tooltip: 'This field is required'
40088                 };
40089                 
40090                 var label = {
40091                     tag: 'label',
40092                     'for':  id,
40093                     cls: 'control-label',
40094                     cn: []
40095                 };
40096                 
40097                 var label_text = {
40098                     tag: 'span',
40099                     html: this.fieldLabel
40100                 };
40101                 
40102                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40103                 label.cn = [
40104                     indicator,
40105                     label_text
40106                 ];
40107                 
40108                 if(this.indicatorpos == 'right') {
40109                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40110                     label.cn = [
40111                         label_text,
40112                         indicator
40113                     ];
40114                 }
40115                 
40116                 if(align == 'left') {
40117                     container = {
40118                         tag: 'div',
40119                         cn: [
40120                             container
40121                         ]
40122                     };
40123                     
40124                     if(this.labelWidth > 12){
40125                         label.style = "width: " + this.labelWidth + 'px';
40126                     }
40127                     if(this.labelWidth < 13 && this.labelmd == 0){
40128                         this.labelmd = this.labelWidth;
40129                     }
40130                     if(this.labellg > 0){
40131                         label.cls += ' col-lg-' + this.labellg;
40132                         input.cls += ' col-lg-' + (12 - this.labellg);
40133                     }
40134                     if(this.labelmd > 0){
40135                         label.cls += ' col-md-' + this.labelmd;
40136                         container.cls += ' col-md-' + (12 - this.labelmd);
40137                     }
40138                     if(this.labelsm > 0){
40139                         label.cls += ' col-sm-' + this.labelsm;
40140                         container.cls += ' col-sm-' + (12 - this.labelsm);
40141                     }
40142                     if(this.labelxs > 0){
40143                         label.cls += ' col-xs-' + this.labelxs;
40144                         container.cls += ' col-xs-' + (12 - this.labelxs);
40145                     }
40146                 }
40147             }
40148             
40149             cfg.cn = [
40150                 label,
40151                 container
40152             ];
40153             
40154             var settings = this;
40155             
40156             ['xs','sm','md','lg'].map(function(size){
40157                 if (settings[size]) {
40158                     cfg.cls += ' col-' + size + '-' + settings[size];
40159                 }
40160             });
40161             
40162             this.store = new Roo.data.Store({
40163                 proxy : new Roo.data.MemoryProxy({}),
40164                 reader : new Roo.data.JsonReader({
40165                     fields : [
40166                         {
40167                             'name' : 'name',
40168                             'type' : 'string'
40169                         },
40170                         {
40171                             'name' : 'iso2',
40172                             'type' : 'string'
40173                         },
40174                         {
40175                             'name' : 'dialCode',
40176                             'type' : 'string'
40177                         },
40178                         {
40179                             'name' : 'priority',
40180                             'type' : 'string'
40181                         },
40182                         {
40183                             'name' : 'areaCodes',
40184                             'type' : 'string'
40185                         }
40186                     ]
40187                 })
40188             });
40189             
40190             if(!this.preferedCountries) {
40191                 this.preferedCountries = [
40192                     'hk',
40193                     'gb',
40194                     'us'
40195                 ];
40196             }
40197             
40198             var p = this.preferedCountries.reverse();
40199             
40200             if(p) {
40201                 for (var i = 0; i < p.length; i++) {
40202                     for (var j = 0; j < this.allCountries.length; j++) {
40203                         if(this.allCountries[j].iso2 == p[i]) {
40204                             var t = this.allCountries[j];
40205                             this.allCountries.splice(j,1);
40206                             this.allCountries.unshift(t);
40207                         }
40208                     } 
40209                 }
40210             }
40211             
40212             this.store.proxy.data = {
40213                 success: true,
40214                 data: this.allCountries
40215             };
40216             
40217             return cfg;
40218         },
40219         
40220         initEvents : function()
40221         {
40222             this.createList();
40223             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40224             
40225             this.indicator = this.indicatorEl();
40226             this.flag = this.flagEl();
40227             this.dialCodeHolder = this.dialCodeHolderEl();
40228             
40229             this.trigger = this.el.select('div.flag-box',true).first();
40230             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40231             
40232             var _this = this;
40233             
40234             (function(){
40235                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40236                 _this.list.setWidth(lw);
40237             }).defer(100);
40238             
40239             this.list.on('mouseover', this.onViewOver, this);
40240             this.list.on('mousemove', this.onViewMove, this);
40241             this.inputEl().on("keyup", this.onKeyUp, this);
40242             this.inputEl().on("keypress", this.onKeyPress, this);
40243             
40244             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40245
40246             this.view = new Roo.View(this.list, this.tpl, {
40247                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40248             });
40249             
40250             this.view.on('click', this.onViewClick, this);
40251             this.setValue(this.defaultDialCode);
40252         },
40253         
40254         onTriggerClick : function(e)
40255         {
40256             Roo.log('trigger click');
40257             if(this.disabled){
40258                 return;
40259             }
40260             
40261             if(this.isExpanded()){
40262                 this.collapse();
40263                 this.hasFocus = false;
40264             }else {
40265                 this.store.load({});
40266                 this.hasFocus = true;
40267                 this.expand();
40268             }
40269         },
40270         
40271         isExpanded : function()
40272         {
40273             return this.list.isVisible();
40274         },
40275         
40276         collapse : function()
40277         {
40278             if(!this.isExpanded()){
40279                 return;
40280             }
40281             this.list.hide();
40282             Roo.get(document).un('mousedown', this.collapseIf, this);
40283             Roo.get(document).un('mousewheel', this.collapseIf, this);
40284             this.fireEvent('collapse', this);
40285             this.validate();
40286         },
40287         
40288         expand : function()
40289         {
40290             Roo.log('expand');
40291
40292             if(this.isExpanded() || !this.hasFocus){
40293                 return;
40294             }
40295             
40296             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40297             this.list.setWidth(lw);
40298             
40299             this.list.show();
40300             this.restrictHeight();
40301             
40302             Roo.get(document).on('mousedown', this.collapseIf, this);
40303             Roo.get(document).on('mousewheel', this.collapseIf, this);
40304             
40305             this.fireEvent('expand', this);
40306         },
40307         
40308         restrictHeight : function()
40309         {
40310             this.list.alignTo(this.inputEl(), this.listAlign);
40311             this.list.alignTo(this.inputEl(), this.listAlign);
40312         },
40313         
40314         onViewOver : function(e, t)
40315         {
40316             if(this.inKeyMode){
40317                 return;
40318             }
40319             var item = this.view.findItemFromChild(t);
40320             
40321             if(item){
40322                 var index = this.view.indexOf(item);
40323                 this.select(index, false);
40324             }
40325         },
40326
40327         // private
40328         onViewClick : function(view, doFocus, el, e)
40329         {
40330             var index = this.view.getSelectedIndexes()[0];
40331             
40332             var r = this.store.getAt(index);
40333             
40334             if(r){
40335                 this.onSelect(r, index);
40336             }
40337             if(doFocus !== false && !this.blockFocus){
40338                 this.inputEl().focus();
40339             }
40340         },
40341         
40342         onViewMove : function(e, t)
40343         {
40344             this.inKeyMode = false;
40345         },
40346         
40347         select : function(index, scrollIntoView)
40348         {
40349             this.selectedIndex = index;
40350             this.view.select(index);
40351             if(scrollIntoView !== false){
40352                 var el = this.view.getNode(index);
40353                 if(el){
40354                     this.list.scrollChildIntoView(el, false);
40355                 }
40356             }
40357         },
40358         
40359         createList : function()
40360         {
40361             this.list = Roo.get(document.body).createChild({
40362                 tag: 'ul',
40363                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40364                 style: 'display:none'
40365             });
40366             
40367             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40368         },
40369         
40370         collapseIf : function(e)
40371         {
40372             var in_combo  = e.within(this.el);
40373             var in_list =  e.within(this.list);
40374             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40375             
40376             if (in_combo || in_list || is_list) {
40377                 return;
40378             }
40379             this.collapse();
40380         },
40381         
40382         onSelect : function(record, index)
40383         {
40384             if(this.fireEvent('beforeselect', this, record, index) !== false){
40385                 
40386                 this.setFlagClass(record.data.iso2);
40387                 this.setDialCode(record.data.dialCode);
40388                 this.hasFocus = false;
40389                 this.collapse();
40390                 this.fireEvent('select', this, record, index);
40391             }
40392         },
40393         
40394         flagEl : function()
40395         {
40396             var flag = this.el.select('div.flag',true).first();
40397             if(!flag){
40398                 return false;
40399             }
40400             return flag;
40401         },
40402         
40403         dialCodeHolderEl : function()
40404         {
40405             var d = this.el.select('input.dial-code-holder',true).first();
40406             if(!d){
40407                 return false;
40408             }
40409             return d;
40410         },
40411         
40412         setDialCode : function(v)
40413         {
40414             this.dialCodeHolder.dom.value = '+'+v;
40415         },
40416         
40417         setFlagClass : function(n)
40418         {
40419             this.flag.dom.className = 'flag '+n;
40420         },
40421         
40422         getValue : function()
40423         {
40424             var v = this.inputEl().getValue();
40425             if(this.dialCodeHolder) {
40426                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40427             }
40428             return v;
40429         },
40430         
40431         setValue : function(v)
40432         {
40433             var d = this.getDialCode(v);
40434             
40435             //invalid dial code
40436             if(v.length == 0 || !d || d.length == 0) {
40437                 if(this.rendered){
40438                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40439                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40440                 }
40441                 return;
40442             }
40443             
40444             //valid dial code
40445             this.setFlagClass(this.dialCodeMapping[d].iso2);
40446             this.setDialCode(d);
40447             this.inputEl().dom.value = v.replace('+'+d,'');
40448             this.hiddenEl().dom.value = this.getValue();
40449             
40450             this.validate();
40451         },
40452         
40453         getDialCode : function(v)
40454         {
40455             v = v ||  '';
40456             
40457             if (v.length == 0) {
40458                 return this.dialCodeHolder.dom.value;
40459             }
40460             
40461             var dialCode = "";
40462             if (v.charAt(0) != "+") {
40463                 return false;
40464             }
40465             var numericChars = "";
40466             for (var i = 1; i < v.length; i++) {
40467               var c = v.charAt(i);
40468               if (!isNaN(c)) {
40469                 numericChars += c;
40470                 if (this.dialCodeMapping[numericChars]) {
40471                   dialCode = v.substr(1, i);
40472                 }
40473                 if (numericChars.length == 4) {
40474                   break;
40475                 }
40476               }
40477             }
40478             return dialCode;
40479         },
40480         
40481         reset : function()
40482         {
40483             this.setValue(this.defaultDialCode);
40484             this.validate();
40485         },
40486         
40487         hiddenEl : function()
40488         {
40489             return this.el.select('input.hidden-tel-input',true).first();
40490         },
40491         
40492         // after setting val
40493         onKeyUp : function(e){
40494             this.setValue(this.getValue());
40495         },
40496         
40497         onKeyPress : function(e){
40498             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40499                 e.stopEvent();
40500             }
40501         }
40502         
40503 });
40504 /**
40505  * @class Roo.bootstrap.MoneyField
40506  * @extends Roo.bootstrap.ComboBox
40507  * Bootstrap MoneyField class
40508  * 
40509  * @constructor
40510  * Create a new MoneyField.
40511  * @param {Object} config Configuration options
40512  */
40513
40514 Roo.bootstrap.MoneyField = function(config) {
40515     
40516     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40517     
40518 };
40519
40520 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40521     
40522     /**
40523      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40524      */
40525     allowDecimals : true,
40526     /**
40527      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40528      */
40529     decimalSeparator : ".",
40530     /**
40531      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40532      */
40533     decimalPrecision : 0,
40534     /**
40535      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40536      */
40537     allowNegative : true,
40538     /**
40539      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40540      */
40541     allowZero: true,
40542     /**
40543      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40544      */
40545     minValue : Number.NEGATIVE_INFINITY,
40546     /**
40547      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40548      */
40549     maxValue : Number.MAX_VALUE,
40550     /**
40551      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40552      */
40553     minText : "The minimum value for this field is {0}",
40554     /**
40555      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40556      */
40557     maxText : "The maximum value for this field is {0}",
40558     /**
40559      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40560      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40561      */
40562     nanText : "{0} is not a valid number",
40563     /**
40564      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40565      */
40566     castInt : true,
40567     /**
40568      * @cfg {String} defaults currency of the MoneyField
40569      * value should be in lkey
40570      */
40571     defaultCurrency : false,
40572     /**
40573      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40574      */
40575     thousandsDelimiter : false,
40576     /**
40577      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40578      */
40579     max_length: false,
40580     
40581     inputlg : 9,
40582     inputmd : 9,
40583     inputsm : 9,
40584     inputxs : 6,
40585     
40586     store : false,
40587     
40588     getAutoCreate : function()
40589     {
40590         var align = this.labelAlign || this.parentLabelAlign();
40591         
40592         var id = Roo.id();
40593
40594         var cfg = {
40595             cls: 'form-group',
40596             cn: []
40597         };
40598
40599         var input =  {
40600             tag: 'input',
40601             id : id,
40602             cls : 'form-control roo-money-amount-input',
40603             autocomplete: 'new-password'
40604         };
40605         
40606         var hiddenInput = {
40607             tag: 'input',
40608             type: 'hidden',
40609             id: Roo.id(),
40610             cls: 'hidden-number-input'
40611         };
40612         
40613         if(this.max_length) {
40614             input.maxlength = this.max_length; 
40615         }
40616         
40617         if (this.name) {
40618             hiddenInput.name = this.name;
40619         }
40620
40621         if (this.disabled) {
40622             input.disabled = true;
40623         }
40624
40625         var clg = 12 - this.inputlg;
40626         var cmd = 12 - this.inputmd;
40627         var csm = 12 - this.inputsm;
40628         var cxs = 12 - this.inputxs;
40629         
40630         var container = {
40631             tag : 'div',
40632             cls : 'row roo-money-field',
40633             cn : [
40634                 {
40635                     tag : 'div',
40636                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40637                     cn : [
40638                         {
40639                             tag : 'div',
40640                             cls: 'roo-select2-container input-group',
40641                             cn: [
40642                                 {
40643                                     tag : 'input',
40644                                     cls : 'form-control roo-money-currency-input',
40645                                     autocomplete: 'new-password',
40646                                     readOnly : 1,
40647                                     name : this.currencyName
40648                                 },
40649                                 {
40650                                     tag :'span',
40651                                     cls : 'input-group-addon',
40652                                     cn : [
40653                                         {
40654                                             tag: 'span',
40655                                             cls: 'caret'
40656                                         }
40657                                     ]
40658                                 }
40659                             ]
40660                         }
40661                     ]
40662                 },
40663                 {
40664                     tag : 'div',
40665                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40666                     cn : [
40667                         {
40668                             tag: 'div',
40669                             cls: this.hasFeedback ? 'has-feedback' : '',
40670                             cn: [
40671                                 input
40672                             ]
40673                         }
40674                     ]
40675                 }
40676             ]
40677             
40678         };
40679         
40680         if (this.fieldLabel.length) {
40681             var indicator = {
40682                 tag: 'i',
40683                 tooltip: 'This field is required'
40684             };
40685
40686             var label = {
40687                 tag: 'label',
40688                 'for':  id,
40689                 cls: 'control-label',
40690                 cn: []
40691             };
40692
40693             var label_text = {
40694                 tag: 'span',
40695                 html: this.fieldLabel
40696             };
40697
40698             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40699             label.cn = [
40700                 indicator,
40701                 label_text
40702             ];
40703
40704             if(this.indicatorpos == 'right') {
40705                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40706                 label.cn = [
40707                     label_text,
40708                     indicator
40709                 ];
40710             }
40711
40712             if(align == 'left') {
40713                 container = {
40714                     tag: 'div',
40715                     cn: [
40716                         container
40717                     ]
40718                 };
40719
40720                 if(this.labelWidth > 12){
40721                     label.style = "width: " + this.labelWidth + 'px';
40722                 }
40723                 if(this.labelWidth < 13 && this.labelmd == 0){
40724                     this.labelmd = this.labelWidth;
40725                 }
40726                 if(this.labellg > 0){
40727                     label.cls += ' col-lg-' + this.labellg;
40728                     input.cls += ' col-lg-' + (12 - this.labellg);
40729                 }
40730                 if(this.labelmd > 0){
40731                     label.cls += ' col-md-' + this.labelmd;
40732                     container.cls += ' col-md-' + (12 - this.labelmd);
40733                 }
40734                 if(this.labelsm > 0){
40735                     label.cls += ' col-sm-' + this.labelsm;
40736                     container.cls += ' col-sm-' + (12 - this.labelsm);
40737                 }
40738                 if(this.labelxs > 0){
40739                     label.cls += ' col-xs-' + this.labelxs;
40740                     container.cls += ' col-xs-' + (12 - this.labelxs);
40741                 }
40742             }
40743         }
40744
40745         cfg.cn = [
40746             label,
40747             container,
40748             hiddenInput
40749         ];
40750         
40751         var settings = this;
40752
40753         ['xs','sm','md','lg'].map(function(size){
40754             if (settings[size]) {
40755                 cfg.cls += ' col-' + size + '-' + settings[size];
40756             }
40757         });
40758         
40759         return cfg;
40760     },
40761     
40762     initEvents : function()
40763     {
40764         this.indicator = this.indicatorEl();
40765         
40766         this.initCurrencyEvent();
40767         
40768         this.initNumberEvent();
40769     },
40770     
40771     initCurrencyEvent : function()
40772     {
40773         if (!this.store) {
40774             throw "can not find store for combo";
40775         }
40776         
40777         this.store = Roo.factory(this.store, Roo.data);
40778         this.store.parent = this;
40779         
40780         this.createList();
40781         
40782         this.triggerEl = this.el.select('.input-group-addon', true).first();
40783         
40784         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40785         
40786         var _this = this;
40787         
40788         (function(){
40789             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40790             _this.list.setWidth(lw);
40791         }).defer(100);
40792         
40793         this.list.on('mouseover', this.onViewOver, this);
40794         this.list.on('mousemove', this.onViewMove, this);
40795         this.list.on('scroll', this.onViewScroll, this);
40796         
40797         if(!this.tpl){
40798             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40799         }
40800         
40801         this.view = new Roo.View(this.list, this.tpl, {
40802             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40803         });
40804         
40805         this.view.on('click', this.onViewClick, this);
40806         
40807         this.store.on('beforeload', this.onBeforeLoad, this);
40808         this.store.on('load', this.onLoad, this);
40809         this.store.on('loadexception', this.onLoadException, this);
40810         
40811         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40812             "up" : function(e){
40813                 this.inKeyMode = true;
40814                 this.selectPrev();
40815             },
40816
40817             "down" : function(e){
40818                 if(!this.isExpanded()){
40819                     this.onTriggerClick();
40820                 }else{
40821                     this.inKeyMode = true;
40822                     this.selectNext();
40823                 }
40824             },
40825
40826             "enter" : function(e){
40827                 this.collapse();
40828                 
40829                 if(this.fireEvent("specialkey", this, e)){
40830                     this.onViewClick(false);
40831                 }
40832                 
40833                 return true;
40834             },
40835
40836             "esc" : function(e){
40837                 this.collapse();
40838             },
40839
40840             "tab" : function(e){
40841                 this.collapse();
40842                 
40843                 if(this.fireEvent("specialkey", this, e)){
40844                     this.onViewClick(false);
40845                 }
40846                 
40847                 return true;
40848             },
40849
40850             scope : this,
40851
40852             doRelay : function(foo, bar, hname){
40853                 if(hname == 'down' || this.scope.isExpanded()){
40854                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40855                 }
40856                 return true;
40857             },
40858
40859             forceKeyDown: true
40860         });
40861         
40862         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40863         
40864     },
40865     
40866     initNumberEvent : function(e)
40867     {
40868         this.inputEl().on("keydown" , this.fireKey,  this);
40869         this.inputEl().on("focus", this.onFocus,  this);
40870         this.inputEl().on("blur", this.onBlur,  this);
40871         
40872         this.inputEl().relayEvent('keyup', this);
40873         
40874         if(this.indicator){
40875             this.indicator.addClass('invisible');
40876         }
40877  
40878         this.originalValue = this.getValue();
40879         
40880         if(this.validationEvent == 'keyup'){
40881             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40882             this.inputEl().on('keyup', this.filterValidation, this);
40883         }
40884         else if(this.validationEvent !== false){
40885             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40886         }
40887         
40888         if(this.selectOnFocus){
40889             this.on("focus", this.preFocus, this);
40890             
40891         }
40892         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40893             this.inputEl().on("keypress", this.filterKeys, this);
40894         } else {
40895             this.inputEl().relayEvent('keypress', this);
40896         }
40897         
40898         var allowed = "0123456789";
40899         
40900         if(this.allowDecimals){
40901             allowed += this.decimalSeparator;
40902         }
40903         
40904         if(this.allowNegative){
40905             allowed += "-";
40906         }
40907         
40908         if(this.thousandsDelimiter) {
40909             allowed += ",";
40910         }
40911         
40912         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40913         
40914         var keyPress = function(e){
40915             
40916             var k = e.getKey();
40917             
40918             var c = e.getCharCode();
40919             
40920             if(
40921                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40922                     allowed.indexOf(String.fromCharCode(c)) === -1
40923             ){
40924                 e.stopEvent();
40925                 return;
40926             }
40927             
40928             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40929                 return;
40930             }
40931             
40932             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40933                 e.stopEvent();
40934             }
40935         };
40936         
40937         this.inputEl().on("keypress", keyPress, this);
40938         
40939     },
40940     
40941     onTriggerClick : function(e)
40942     {   
40943         if(this.disabled){
40944             return;
40945         }
40946         
40947         this.page = 0;
40948         this.loadNext = false;
40949         
40950         if(this.isExpanded()){
40951             this.collapse();
40952             return;
40953         }
40954         
40955         this.hasFocus = true;
40956         
40957         if(this.triggerAction == 'all') {
40958             this.doQuery(this.allQuery, true);
40959             return;
40960         }
40961         
40962         this.doQuery(this.getRawValue());
40963     },
40964     
40965     getCurrency : function()
40966     {   
40967         var v = this.currencyEl().getValue();
40968         
40969         return v;
40970     },
40971     
40972     restrictHeight : function()
40973     {
40974         this.list.alignTo(this.currencyEl(), this.listAlign);
40975         this.list.alignTo(this.currencyEl(), this.listAlign);
40976     },
40977     
40978     onViewClick : function(view, doFocus, el, e)
40979     {
40980         var index = this.view.getSelectedIndexes()[0];
40981         
40982         var r = this.store.getAt(index);
40983         
40984         if(r){
40985             this.onSelect(r, index);
40986         }
40987     },
40988     
40989     onSelect : function(record, index){
40990         
40991         if(this.fireEvent('beforeselect', this, record, index) !== false){
40992         
40993             this.setFromCurrencyData(index > -1 ? record.data : false);
40994             
40995             this.collapse();
40996             
40997             this.fireEvent('select', this, record, index);
40998         }
40999     },
41000     
41001     setFromCurrencyData : function(o)
41002     {
41003         var currency = '';
41004         
41005         this.lastCurrency = o;
41006         
41007         if (this.currencyField) {
41008             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41009         } else {
41010             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41011         }
41012         
41013         this.lastSelectionText = currency;
41014         
41015         //setting default currency
41016         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41017             this.setCurrency(this.defaultCurrency);
41018             return;
41019         }
41020         
41021         this.setCurrency(currency);
41022     },
41023     
41024     setFromData : function(o)
41025     {
41026         var c = {};
41027         
41028         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41029         
41030         this.setFromCurrencyData(c);
41031         
41032         var value = '';
41033         
41034         if (this.name) {
41035             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41036         } else {
41037             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41038         }
41039         
41040         this.setValue(value);
41041         
41042     },
41043     
41044     setCurrency : function(v)
41045     {   
41046         this.currencyValue = v;
41047         
41048         if(this.rendered){
41049             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41050             this.validate();
41051         }
41052     },
41053     
41054     setValue : function(v)
41055     {
41056         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41057         
41058         this.value = v;
41059         
41060         if(this.rendered){
41061             
41062             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41063             
41064             this.inputEl().dom.value = (v == '') ? '' :
41065                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41066             
41067             if(!this.allowZero && v === '0') {
41068                 this.hiddenEl().dom.value = '';
41069                 this.inputEl().dom.value = '';
41070             }
41071             
41072             this.validate();
41073         }
41074     },
41075     
41076     getRawValue : function()
41077     {
41078         var v = this.inputEl().getValue();
41079         
41080         return v;
41081     },
41082     
41083     getValue : function()
41084     {
41085         return this.fixPrecision(this.parseValue(this.getRawValue()));
41086     },
41087     
41088     parseValue : function(value)
41089     {
41090         if(this.thousandsDelimiter) {
41091             value += "";
41092             r = new RegExp(",", "g");
41093             value = value.replace(r, "");
41094         }
41095         
41096         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41097         return isNaN(value) ? '' : value;
41098         
41099     },
41100     
41101     fixPrecision : function(value)
41102     {
41103         if(this.thousandsDelimiter) {
41104             value += "";
41105             r = new RegExp(",", "g");
41106             value = value.replace(r, "");
41107         }
41108         
41109         var nan = isNaN(value);
41110         
41111         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41112             return nan ? '' : value;
41113         }
41114         return parseFloat(value).toFixed(this.decimalPrecision);
41115     },
41116     
41117     decimalPrecisionFcn : function(v)
41118     {
41119         return Math.floor(v);
41120     },
41121     
41122     validateValue : function(value)
41123     {
41124         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41125             return false;
41126         }
41127         
41128         var num = this.parseValue(value);
41129         
41130         if(isNaN(num)){
41131             this.markInvalid(String.format(this.nanText, value));
41132             return false;
41133         }
41134         
41135         if(num < this.minValue){
41136             this.markInvalid(String.format(this.minText, this.minValue));
41137             return false;
41138         }
41139         
41140         if(num > this.maxValue){
41141             this.markInvalid(String.format(this.maxText, this.maxValue));
41142             return false;
41143         }
41144         
41145         return true;
41146     },
41147     
41148     validate : function()
41149     {
41150         if(this.disabled || this.allowBlank){
41151             this.markValid();
41152             return true;
41153         }
41154         
41155         var currency = this.getCurrency();
41156         
41157         if(this.validateValue(this.getRawValue()) && currency.length){
41158             this.markValid();
41159             return true;
41160         }
41161         
41162         this.markInvalid();
41163         return false;
41164     },
41165     
41166     getName: function()
41167     {
41168         return this.name;
41169     },
41170     
41171     beforeBlur : function()
41172     {
41173         if(!this.castInt){
41174             return;
41175         }
41176         
41177         var v = this.parseValue(this.getRawValue());
41178         
41179         if(v || v == 0){
41180             this.setValue(v);
41181         }
41182     },
41183     
41184     onBlur : function()
41185     {
41186         this.beforeBlur();
41187         
41188         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41189             //this.el.removeClass(this.focusClass);
41190         }
41191         
41192         this.hasFocus = false;
41193         
41194         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41195             this.validate();
41196         }
41197         
41198         var v = this.getValue();
41199         
41200         if(String(v) !== String(this.startValue)){
41201             this.fireEvent('change', this, v, this.startValue);
41202         }
41203         
41204         this.fireEvent("blur", this);
41205     },
41206     
41207     inputEl : function()
41208     {
41209         return this.el.select('.roo-money-amount-input', true).first();
41210     },
41211     
41212     currencyEl : function()
41213     {
41214         return this.el.select('.roo-money-currency-input', true).first();
41215     },
41216     
41217     hiddenEl : function()
41218     {
41219         return this.el.select('input.hidden-number-input',true).first();
41220     }
41221     
41222 });